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