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
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:






















3 ComentĂĄrios
Fernando.. nĂŁo tem uma biblioteca do tlc5940 para oedp32?
ResponderExcluirEsp32
ResponderExcluirbom dia!
ResponderExcluirpra eu usar o mpx5100 para acender uma barra de led preciso programa-lo?
seria tipo um vultimetro (VU) com LM3914