Recents in Beach


Receba o meu conteĂșdo GRATUITAMENTE


Controle de volume por WiFi



Controle um volume digital por WiFi!
Imagine que vocĂȘ tem uma caixa de som em um outro ambiente e quer controlar o volume deste sistema de som atravĂ©s de um smartphone. Isso Ă© possĂ­vel por meio de um ESP32. Vou, entĂŁo, te apresentar as chaves analĂłgicas de estado sĂłlido e possĂ­veis aplicaçÔes, utilizar o CD4066 para controlar o nĂ­vel de um sinal e implementar um controle via WiFi para o CD4066. Destaco que esse exemplo nĂŁo serve somente para controle de volume, mas pode tambĂ©m chavear um vĂ­deo analĂłgico.





Demonstração





Recursos usados

·         Um ESP WiFi LoRa 32
·         Um LM386 – Amplificador de ĂĄudio de baixa tensĂŁo e consumo
·         Um CD4066 – Quatro chaves bilaterais analĂłgicas
·         Seis resistores
·         Um capacitor
·         Fios para conexĂ”es
·         Um protoboard
·         Um cabo USB para o ESP




Chaves analĂłgicas de estado sĂłlido

·         Amplamente utilizadas em aplicaçÔes que necessitam de multiplexação e/ou controle de mĂșltiplos canais, sejam para aquisição de dados, controle de processos, sistemas de ĂĄudio e vĂ­deo, telecomunicaçÔes e etc.
·         Inicialmente projetadas e produzidas com componentes MOSFET discretos (final dos anos 60) e usados em mĂłdulos ou placas, tornaram-se circuitos integrados acompanhando a evolução dos processos de fabricação, dando origem a vĂĄrias famĂ­lias de ci’s.
·         As melhorias alcançadas foram: a redução da resistĂȘncia (Ron) da chave, o chaveamento rĂĄpido para circuitos de alta frequĂȘncia, menor tensĂŁo de alimentação e potĂȘncia dissipada, menor custo, miniaturização.


Circuito bĂĄsico de uma chave CMOS usando um par complementar para reduzir o Ron (Analog Switches and Multiplexers Basics Tutorial – Anolog Devices)



CD4066

·         Existem vĂĄrios CI’s de chaves analĂłgicas com diversas propriedades diferentes, adequadas a uma diversidade de aplicaçÔes, como MAX4660 que podem substituir relĂ©s   para baixas correntes (atĂ© 150mA) ou HV2631 que possui 16 canais de alta tensĂŁo (220V).
·         Vamos utilizar o CD4066, devido ao seu baixo custo, facilidade de ser encontrado e por ser um circuito muito conhecido.




CD4066 – Circuito interno

·         É formado por um circuito de controle e um circuito de chaveamento.
·         Os quatro canais de sinais (SIG) sĂŁo bidirecionais.
·         Os controles ativam as chaves quando em nĂ­vel alto.
·         Quando desligadas as chaves apresentam alta impedĂąncia.
·         Quando fechadas possuem Ron de aproximadamente 125ohms






·         Disposição das  chaves no CD4066






Exemplos de aplicaçÔes

Amp-op inversor com ganho selecionĂĄvel


Seletor de 4 sinais para um Ășnico ADC (ex. ESP8266)

Chaveamento de sinais




Controlando o nĂ­vel do sinal de entrada de um amplificador de ĂĄudio

·         Como exemplo prĂĄtico, vamos controlar o nĂ­vel do sinal de entrada de um amplificado de ĂĄudio.
·         Para isso vamos utilizar um amplificador de ĂĄudio bastante conhecido e de fĂĄcil implementação, o LM386.
·         O LM386 possui baixo consumo, funciona com baixas tensĂ”es e possui um ganho fixado em 20, mas que pode ser ajustado de 20 a 200 atravĂ©s dos pinos 1 e 8.
·         Neste exemplo utilizaremos o ganho fixo de 20, e mudaremos o nĂ­vel do sinal de entrada, mas seria possĂ­vel aplicar a mesma ideia alterando o ganho do LM386.

Pin-out LM386


Circuito bĂĄsico de ganho 20 sugerido pelo fabricante




Controlando o nĂ­vel do sinal de entrada de um amplificador de ĂĄudio – Esquema utilizado

·         SubstituĂ­mos o potenciĂŽmetro da entrada por um divisor de tensĂŁo selecionĂĄvel



Tabela do divisor de tensĂŁo

·         O cĂĄlculo do divisor Ă© simples mas pode ser um tanto enfadonho.
·         É importante levar em consideração as resistĂȘncias apresentadas pelas chaves (aproximadamente 125 ohms).
·         Se as resistĂȘncias do divisor forem altas o bastante, as resistĂȘncias das chaves podem ser irrelevantes no divisor e poderĂŁo ser ignoradas.
·         Opte sempre por configuraçÔes em que as resistĂȘncias das chaves nĂŁo influenciem o circuito, removendo assim uma variĂĄvel de seu projeto.


Os valores apresentados na tabela e utilizados no circuito foram escolhidos para atender a demonstração. Os parùmetros foram a adequação do sinal de entrada måximo para o amplificador dentro da faixa sugerida pelo fabricante (+-0,4Vpp), a disponibilidade de resistores (valores comerciais).



CD4066 versus potenciĂŽmetros digitais

·         Para uma aplicação mais refinada, torna-se mais interessante a aplicação de potenciĂŽmetros digitais. Estes dispositivos especializados superam em muitos aspectos a abordagem apresentada aqui.
·         Tomemos como exemplo a famĂ­lia X9C:
·         SĂŁo potenciĂŽmetros de estado sĂłlido
·         Utilizam uma interface serial de trĂȘs fios (chip select, incrementar/decrementar e incremento).
·         Podem ser utilizados como potenciĂŽmetros de trĂȘs terminais ou resistores variĂĄveis de dois terminais.
·         Possuem uma matriz interna de 99 resistores.
·         Podem ser encontrados em valores de 1k, 10k, 50k e 100k ohms. (X9C102, X9C103, X9C503 e X9C104 respectivamente.)
·         Podem armazenar um estado em uma memĂłria interna nĂŁo volĂĄtil.
·         Nossa escolha por esta abordagem Ă© simplesmente didĂĄtica, jĂĄ que a Ășnica vantagem seria a simples disponibilidade do CD4066.
·         AlĂ©m do mais, esta demonstração pode servir de base para outras formas de controle.




CĂłdigo-fonte do teste de todos os nĂ­veis (sem conexĂŁo WiFi)


CĂłdigo-fonte: #includes e #defines

//Bibliotecas para utilização do display OLED
#include <Wire.h>  // NecessĂĄrio apenas para o Arduino 1.6.5 e posterior
#include "SSD1306.h" // o mesmo que #include "SSD1306Wire.h"

//Os pinos do OLED estĂŁo conectados ao ESP32 pelos seguintes GPIO's:
//OLED_SDA -- GPIO4
//OLED_SCL -- GPIO15
//OLED_RST -- GPIO16

#define SDA    4
#define SCL   15
#define RST   16 //RST deve ser ajustado por software



CĂłdigo-fonte: Objetos, constantes e variĂĄveis

//Instanciando e ajustando os pinos do objeto "display"
SSD1306  display(0x3c, SDA, SCL, RST);

const int pinoA = 21; //pino A
const int pinoB = 13; //pino B
const int pinoC = 12; //pino C
const int pinoD = 14; //pino D

//VariĂĄveis que guardam os estados dos pinos
boolean statusA = true;
boolean statusB = true;
boolean statusC = true;
boolean statusD = true;

//Variåvel que determina a seleção
byte selecao = 0;


CĂłdigo-fonte: Setup()

void setup()
{
  pinMode(pinoA, OUTPUT); //pino 21 como saĂ­da
  pinMode(pinoB, OUTPUT); //pino 13 como saĂ­da
  pinMode(pinoC, OUTPUT); //pino 12 como saĂ­da
  pinMode(pinoD, OUTPUT); //pino 14 como saĂ­da

  // Inicia o display
  display.init();

  //Vira a tela verticalmente
  display.flipScreenVertically();
}


CĂłdigo-fonte: Loop() – Switch(selecao)

void loop()
{
  //pelo valor da seleção, determina o estado dos pinos
  switch (selecao)
  {
    case 0:
      statusA = 0;
      statusB = 0;
      statusC = 0;
      statusD = 0;
      break;
    case 1:
      statusA = 0;
      statusB = 0;
      statusC = 0;
      statusD = 1;
      break;
    case 2:
      statusA = 0;
      statusB = 0;
      statusC = 1;
      statusD = 0;
      break;
    case 3:
      statusA = 0;
      statusB = 0;
      statusC = 1;
      statusD = 1;
      break;
    case 4:
      statusA = 0;
      statusB = 1;
      statusC = 0;
      statusD = 0;
      break;
    case 5:
      statusA = 0;
      statusB = 1;
      statusC = 0;
      statusD = 1;
      break;
    case 6:
      statusA = 0;
      statusB = 1;
      statusC = 1;
      statusD = 0;
      break;
    case 7:
      statusA = 0;
      statusB = 1;
      statusC = 1;
      statusD = 1;
      break;
    case 8:
      statusA = 1;
      statusB = 0;
      statusC = 0;
      statusD = 0;
      break;
    case 9:
      statusA = 1;
      statusB = 0;
      statusC = 0;
      statusD = 1;
      break;
    case 10:
      statusA = 1;
      statusB = 0;
      statusC = 1;
      statusD = 0;
      break;
    case 11:
      statusA = 1;
      statusB = 0;
      statusC = 1;
      statusD = 1;
      break;
    case 12:
      statusA = 1;
      statusB = 1;
      statusC = 0;
      statusD = 0;
      break;
    case 13:
      statusA = 1;
      statusB = 1;
      statusC = 0;
      statusD = 1;
      break;
    case 14:
      statusA = 1;
      statusB = 1;
      statusC = 1;
      statusD = 0;
      break;
    case 15:
      statusA = 1;
      statusB = 1;
      statusC = 1;
      statusD = 1;
      break;
  }


CĂłdigo-fonte: Loop() – Ajusta os pinos e incrementa

/*Ajusta pinos com inversĂŁo para que
    o estado 1 represente o resistor incluido
    no divisor de tensĂŁo.
    (somente por conveniĂȘncia)
  */
  digitalWrite(pinoA, !statusA);
  digitalWrite(pinoB, !statusB);
  digitalWrite(pinoC, !statusC);
  digitalWrite(pinoD, !statusD);

  //incrementa a seleção para varrer todos os estados
  selecao++;
  
  //verifica se o décimo sexto estado foi atingido
  if (selecao > 15) {
    selecao = 0;//se verdadeiro, volta para o primeiro estado
  }

*Observe a inversĂŁo ao ajustar os pinos. Essa inversĂŁo foi colocada somente por conveniĂȘncia, para que um bit alto representasse o resistor participando do divisor e nĂŁo o contrĂĄrio.


CĂłdigo-fonte: Loop() – Mostra os valores apĂłs 5s de ativação 

if (millis() > (5000)) //se estĂĄ ligado a mais que 5 segundos
  {
    //Limpa o buffer do display
    display.clear();
    //ajusta o alinhamento para a esquerda
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    //ajusta a fonte para Arial 16
    display.setFont(ArialMT_Plain_16);
    //Escreve no buffer do display 
    display.drawString(0, 0, "Seleção atual");
    display.drawString(0, 16, "N  ABCD");
    display.drawString(0, 32, String(selecao) + "  " + String(statusA) + String(statusB) + String(statusC) + String(statusD));
  }


Código-fonte: Tela inicial exibida por 5s após ativação


else //se estĂĄ ligado a menos de 5 segundos, exibe a tela inicial
  {
    //limpa o buffer do display
    display.clear();
    //Ajusta o alinhamento para a esquerda
    display.setTextAlignment(TEXT_ALIGN_CENTER);
    //ajusta a fonte para Arial 16
    display.setFont(ArialMT_Plain_16);
    //escreve no buffer
    display.drawString(64, 0, "Ajuste de nĂ­vel");
    //escreve no buffer
    display.drawString(64, 18, "com CD4066");
    //ajusta a fonte para Arial 10
    display.setFont(ArialMT_Plain_10);
    //escreve no buffer
    display.drawString(64, 44, "ESP-WiFi-Lora");
  }
  display.display();//transfere o buffer para o display

  delay(100);//aguarda um momento antes de continuar
}


CĂłdigo-fonte do controle via http

CĂłdigo-fonte: #includes e #defines

//Inclui biblioteca Wifi
#include <WiFi.h>
//Bibliotecas para utilização do display OLED
#include <Wire.h>  // NecessĂĄrio apenas para o Arduino 1.6.5 e posterior
#include "SSD1306.h" // o mesmo que #include "SSD1306Wire.h"

//Os pinos do OLED estĂŁo conectados ao ESP32 pelos seguintes GPIO's:
//OLED_SDA -- GPIO4
//OLED_SCL -- GPIO15
//OLED_RST -- GPIO16

#define SDA    4
#define SCL   15
#define RST   16 //RST deve ser ajustado por software


CĂłdigo-fonte: Objetos, constantes e variĂĄveis

//Instanciando e ajustando os pinos do objeto "display"
SSD1306  display(0x3c, SDA, SCL, RST);

//constantes que representam os pinos
const int pinoA = 21; //pino A
const int pinoB = 13; //pino B
const int pinoC = 12; //pino C
const int pinoD = 14; //pino D

//VariĂĄveis que guardam os estados dos pinos
boolean statusA = true;
boolean statusB = true;
boolean statusC = true;
boolean statusD = true;

//Cria um server na porta 80
//(porta padrão para onde os navegadores enviam as requisiçÔes http)
WiFiServer server(80);


CĂłdigo-fonte: Setup() – saĂ­das e mensagem inicial

void setup()
{
  pinMode(pinoA, OUTPUT); //pino 21 como saĂ­da
  pinMode(pinoB, OUTPUT); //pino 13 como saĂ­da
  pinMode(pinoC, OUTPUT); //pino 12 como saĂ­da
  pinMode(pinoD, OUTPUT); //pino 14 como saĂ­da

  // Inicia o display
  display.init();

  //Vira a tela verticalmente
  display.flipScreenVertically();

  //Limpa o buffer do display
  display.clear();

  //ajusta o alinhamento para a esquerda
  display.setTextAlignment(TEXT_ALIGN_LEFT);

  //ajusta a fonte para Arial 16
  display.setFont(ArialMT_Plain_16);

  //Escreve no buffer do display
  display.drawString(0, 0, "Conectando");

  //Exibe o buffer
  display.display();


CĂłdigo-fonte: Setup() - tenta se conectar...

//Faz o ESP se conectar Ă  rede WiFi disponĂ­vel no local de uso.
  //No nosso exemplo o ssid da rede Ă© redeWiFi e a senha Ă© senhateste
  WiFi.begin("redeWiFi", "senhateste");

  //Enquanto o ESP nĂŁo se conectar Ă  rede
  while (WiFi.status() != WL_CONNECTED)
  {
    //Esperamos 100 milisegundos
    delay(100);
    display.drawString(0, 16, ".");
    display.display();
  }

  //Se chegou aqui Ă© porque conectou Ă  rede,
  //entĂŁo mostramos no display para termos um feedback
  display.clear();
  display.drawString(0, 0, "Conectou!");
  display.display();
  delay(1000);


CĂłdigo-fonte: Setup() – configuraçÔes de rede

//ConfiguraçÔes do IP fixo.
  //VocĂȘ pode alterar conforme a sua rede.
  IPAddress ip(192, 168, 0, 119);
  IPAddress gateway(192, 168, 0, 1);
  IPAddress subnet(255, 255, 255, 0);

  display.clear();
  display.drawString(0, 0, "Configurando IP");
  display.drawString(0, 16, "fixo para : ");
  display.drawString(0, 32, ip.toString());
  display.display();

  //Envia a configuração
  WiFi.config(ip, gateway, subnet);

  //Inicializa o server que criamos na porta 80
  server.begin();

  delay(1000);
  //Mostramos no display o IP que o ESP possui
  //para verificarmos se Ă© o mesmo que configuramos
  display.clear();
  display.drawString(0, 0, "Server em:");
  display.drawString(0, 16, WiFi.localIP().toString());
  display.display();
}


CĂłdigo-fonte: Loop() – Aguarda por um cliente...

void loop()
{
  //Verifica se algum cliente estĂĄ tentando se conectar
  WiFiClient client = server.available();
  if (!client)
  {
    //Se nĂŁo houver nenhum cliente podemos retornar pois nĂŁo hĂĄ nada a fazer
    return;
  }

  //Mas se um cliente se conectar, atualizamos o display
  display.clear();
  display.drawString(0, 0, "Server em:");
  display.drawString(0, 16, WiFi.localIP().toString());
  display.drawString(0, 32, "Novo cliente");
  display.drawString(0, 48, "conectado!");
  display.display();

  //Fazemos a leitura da requisição
  String req = client.readStringUntil('\r');


CĂłdigo-fonte: Loop() – Prepara o html da pĂĄgina (parte 1)

//Este Ă© o html que iremos retornar para o cliente
  //É composto basicamente de botĂ”es numerados indicando os nĂ­veis de 0 a 15
  //A parte que nos interessa é o <a href=' com a ação vinculada a cada botão
  //Quando clicamos em um destes botÔes essa informação chegarå até o ESP para
  //que ele verifique qual ação deve executar
  //A parte dentro de '<style>' Ă© apenas para modificarmos o visual da pĂĄgina
  //que serĂĄ exibida, vocĂȘ pode alterĂĄ-la como queira
  String html =
    "<html>"
    "<head>"
    "<meta name='viewport' content='width=device-width, initial-scale=1, user-scalable=no'/>"
    "<title>Controle de nĂ­vel de sinal</title>"
    "<style>"
    "body{"
    "text-align: center;"
    "font-family: sans-serif;"
    "font-size:25px;"
    "padding: 25px;"
    "}"
    "p{"
    "color:#444;"
    "}"
    "button{"
    "outline: none;"
    "border: 2px solid #1fa3ec;"
    "border-radius:18px;"
    "background-color:#FFF;"
    "color: #1fa3ec;"
    "padding: 5px 25px;"
    "}"
    "button:active{"
    "color: #fff;"
    "background-color:#1fa3ec;"
    "}"
    "button:hover{"
    "border-color:#0000ff;"
    "}"


CĂłdigo-fonte: Loop() – Prepara o html da pĂĄgina (parte 2)

"</style>"
    "</head>"
    "<body>"
    "<p>Escolha o nivel</p>"
    "<p><a href='?acao=0'><button>0</button></a>"
    "<a href='?acao=1'><button>1</button></a>"
    "<a href='?acao=2'><button>2</button></a>"
    "<a href='?acao=3'><button>3</button></a></p>"
    "<p><a href='?acao=4'><button>4</button></a>"
    "<a href='?acao=5'><button>5</button></a>"
    "<a href='?acao=6'><button>6</button></a>"
    "<a href='?acao=7'><button>7</button></a></p>"
    "<p><a href='?acao=8'><button>8</button></a>"
    "<a href='?acao=9'><button>9</button></a>"
    "<a href='?acao=A'><button>10</button></a>"
    "<a href='?acao=B'><button>11</button></a></p>"
    "<p><a href='?acao=C'><button>12</button></a>"
    "<a href='?acao=D'><button>13</button></a>"
    "<a href='?acao=E'><button>14</button></a>"
    "<a href='?acao=F'><button>15</button></a></p>"
    "</body>"
    "</html>";
  //Escreve o html no buffer que serĂĄ enviado para o cliente
  client.print(html);
  //Envia os dados do buffer para o cliente
  client.flush();


CĂłdigo-fonte: Loop() – Imagem da pĂĄgina servida



CĂłdigo-fonte: Loop() – Avalia a requisição

//A partir daqui, verificamos se a requisição possui algum comando de
  //ajuste de sinal
  if (req.indexOf("acao=0") != -1)
  {
    statusA = 0;
    statusB = 0;
    statusC = 0;
    statusD = 0;
  }
  else if (req.indexOf("acao=1") != -1)
  {
    statusA = 0;
    statusB = 0;
    statusC = 0;
    statusD = 1;
  }

  if (req.indexOf("acao=2") != -1)
  {
    statusA = 0;
    statusB = 0;
    statusC = 1;
    statusD = 0;
  }
  else if (req.indexOf("acao=3") != -1)
  {
    statusA = 0;
    statusB = 0;
    statusC = 1;
    statusD = 1;
  }

  if (req.indexOf("acao=4") != -1)
  {
    statusA = 0;
    statusB = 1;
    statusC = 0;
    statusD = 0;
  }
  else if (req.indexOf("acao=5") != -1)
  {
    statusA = 0;
    statusB = 1;
    statusC = 0;
    statusD = 1;
  }

  if (req.indexOf("acao=6") != -1)
  {
    statusA = 0;
    statusB = 1;
    statusC = 1;
    statusD = 0;
  }
  else if (req.indexOf("acao=7") != -1)
  {
    statusA = 0;
    statusB = 1;
    statusC = 1;
    statusD = 1;
  }

  if (req.indexOf("acao=8") != -1)
  {
    statusA = 1;
    statusB = 0;
    statusC = 0;
    statusD = 0;
  }
  else if (req.indexOf("acao=9") != -1)
  {
    statusA = 1;
    statusB = 0;
    statusC = 0;
    statusD = 1;
  }

  if (req.indexOf("acao=A") != -1)
  {
    statusA = 1;
    statusB = 0;
    statusC = 1;
    statusD = 0;
  }
  else if (req.indexOf("acao=B") != -1)
  {
    statusA = 1;
    statusB = 0;
    statusC = 1;
    statusD = 1;
  }

  if (req.indexOf("acao=C") != -1)
  {
    statusA = 1;
    statusB = 1;
    statusC = 0;
    statusD = 0;
  }
  else if (req.indexOf("acao=D") != -1)
  {
    statusA = 1;
    statusB = 1;
    statusC = 0;
    statusD = 1;
  }

  if (req.indexOf("acao=E") != -1)
  {
    statusA = 1;
    statusB = 1;
    statusC = 1;
    statusD = 0;
  }
  else if (req.indexOf("acao=F") != -1)
  {
    statusA = 1;
    statusB = 1;
    statusC = 1;
    statusD = 1;
  }


CĂłdigo-fonte: Loop() – ajusta e finaliza a conexĂŁo

 //Ajusta os estados dos pinos (a inversĂŁo segue o princĂ­pio descrito anteriormente)
  digitalWrite(pinoA, !statusA);
  digitalWrite(pinoB, !statusB);
  digitalWrite(pinoC, !statusC);
  digitalWrite(pinoD, !statusD);

  //aguarda para que o trafego das informaçÔes seja concluído
  delay(200);
  //Fecha a conexĂŁo com o cliente
  client.stop();
  //Ao desconectar o cliente, atualiza o display
  display.clear();
  display.drawString(0, 0, "Server em:");
  display.drawString(0, 16, WiFi.localIP().toString());
  display.display();

}


CĂłdigo-fonte: Loop() – Aguardando cliente...





Faça o download dos arquivos:




Postar um comentĂĄrio

3 ComentĂĄrios

  1. Fernando.. nĂŁo tem uma biblioteca do tlc5940 para oedp32?

    ResponderExcluir
  2. bom dia!
    pra eu usar o mpx5100 para acender uma barra de led preciso programa-lo?
    seria tipo um vultimetro (VU) com LM3914

    ResponderExcluir