sábado, 18 de junho de 2016

Configurando WiFi do ESP8266 Sem Recompilar


Pessoal, nesse terceiro artigo da série sobre o ESP8266 vou ensinar a configurar a Rede WiFi através do Navegador de Internet, sem precisar recompilar o código toda vez que mudamos o SSID e a senha do roteador.

Aprendemos no primeiro artigo a mapear funções do ESP8266 para páginas HTML e formulários que podem ser acessados por um Navegador de Internet.

E como já aprendemos no segundo artigo, podemos criar um formulário e enviar informações para o ESP8266 através de métodos como GET ou POST.

Nesse caso, utilizamos um formulário para preencher o SSID, senha, IP e Gateway e enviar via POST para o ESP8266 mudar suas configurações em tempo real.

Primeiro vamos ver como isso funciona e depois veremos o código necessário para isso.

Antes de mais nada precisamos configurar nosso roteador com as configurações iniciais do ESP8266 para fazermos a primeira conexão e depois podemos configurar os dados novos.

  • 1) Acesse a página de configuração do seu roteador e clique na opção das configurações sem fio
  • 2) Altere o nome do SSID para SSIDInicial
  • 3) Altere a opção de segurança para WPA-PSK [TKIP] + WPA2-PSK [AES]
  • 4) Mude também a senha para senhaInicial
Clique em "Enviar" para confirmar as configurações do seu roteador. Isso fará com que o roteador fique com as mesmas configurações iniciais que vamos escrever no código mais adiante.

Na sequência precisamos configurar o IP do roteador, que é o Gateway que configuramos no ESP8266.


  • 1) Clique na opção de configuração de IP da LAN (os nomes podem ser um pouco diferentes dependendo da versão do roteador)
  • 2) Configure o Endereço IP para 192.168.0.1
Feita essa configuração, você pode carregar o programa desse post e abrir o terminal Serial para verificar se o ESP8266 conectou no roteador corretamente, conforme imagem abaixo.



Ligue seu computador via cabo ou através do WiFi neste mesmo roteador. E abra um Navegador de Internet. Acesse o endereço do ESP8266, que na configuração inicial do código deste artigo é 192.168.0.125. Configure no código o endereço inicial que você deseja para seu ESP8266.


Clique no link "Configurar WiFi" ou acesse o endereço http://192.168.0.125/config


Pronto! Agora podemos mudar as configurações para o que quisermos sem precisar recompilar o código! Digite as informações que você quiser e clique em "Enviar"!


Isso parece simples, mas é muito poderoso, pois quando você cria um produto que oferece uma funcionalidade de WiFi, como você dá oportunidade para seu cliente configurar o produto para funcionar na rede dele? Esse é o jeito! Além disso com isso podemos oferecer para o cliente uma forma de atualizar o próprio firmware do ESP8266, que veremos no próximo post da série.

Vamos ver como isso é feito!


A primeira coisa que devemos fazer é incluir a biblioteca EEPROM, pois não tem sentido fazer toda essa configuração e depois perder ela quando a energia acabar. Para que as configurações permaneçam gravadas mesmo quando o ESP8266 for desligado, precisamos gravar essas informações na memória conhecida por EEPROM.

O resto do código é, na verdade, composto de coisas que já aprendemos nos outros dois posts. Vejamos os detalhes.


  • 1) Definimos algumas macros para marcar posições iniciais da memória EEPROM onde ficarão nosso banco de dados de configuração. Temos uma macro para o início do controle de versão VERSION_START e uma macro para indicar onde a configuração realmente inicia CONFIG_START.
  • 2) Criamos também uma macro para controlar a versão dos dados CONFIG_VERSION. Como a EEPROM grava os dados e não perde mais, temos esse controle para invalidar os dados anteriores em caso de recompilar o código, se necessário.
  • Por fim, criamos uma estrutura para guardar as configurações, chamada wifiConfig. Utilizaremos essa estrutura para facilitar o trabalho com a EEPROM, pois senão teríamos que lidar com leitura e escrita de bytes, o que é muito massante.

Para ajudar com a tarefa massante de lidar com gravação da EEPROM, criamos também uma função que utiliza um laço for para gravar todos os bytes da estrutura e facilitar o trabalho. Repare também que gravamos os bytes de controle da versão; e por fim utilizamos a função EEPROM.commit() para confirmar a gravação.


Outra função importante para lidar com a EEPROM é a que faz a leitura e alimenta a estrutura wifiConfig. Primeiro a função faz a leitura e verifica os bytes de controle para saber se existem e se conferem com o código atual. Lembre-se, para invalidar esse código, basta mudar a macro mostrada anteriormente de "1a" para qualquer outro valor, como por exemplo "1b" ou "JJ", não importa.

Invalidando esses bytes de controle farão com que o ESP8266 utilize os dados da configuração inicial, que é fixa. Por isso configuramos o roteador anteriormente com essas configurações no início desse post.
  • 1) SSIDInicial é o SSID da configuração inicial fixa no código
  • 2) senhaInicial é a senha inicial
  • 3) 192.168.0.125 é o IP inicial do ESP8266
  • 4) 192.168.0.1 é o IP do roteador, ou também chamado IP do Gateway
Essa configuração do código dentro do else só é carregada na primeira vez que o ESP8266 inicia. Depois disso as alterações são feitas pelo Navegador, conforme mostrado anteriormente e depois lida pela parte do if.

Quando acessamos a página de configuração do WiFi, chamamos a função mostrada abaixo, que monta e preenche o formulário HTML com as informações da estrutura wifiConfig, carregadas da EEPROM.


Ao clicar no botão "Enviar" desse formulário, a outra função que chamada é a mostrada abaixo, onde os dados preenchidos são registrados na estrutura wifiConfig e depois gravados na EEPROM, através da função saveConfig() que vimos anteriormente.


Repare que a última instrução é uma função especial para reiniciar o ESP8266 chamada ESP.restart(). Isso faz com que o WiFi reinicie e tente conectar de novo no roteador, com os dados novos que foram informados.

Quando o ESP8266 reinicia, a função setup() é chamada novamente, então as configurações carregadas são as novas que foram gravadas na EEPROM, conforme vemos a seguir.


Veja que precisamos primeiro iniciar a EEPROM chamando a função EEPROM.begin(), passando o valor 512 que é o tamanho do espaço reservado para essa memória no seu ESP8266. Verifique o tamanho da versão que você estiver usando.

Depois temos a chamada da funão loadConfig() que faz a leitura da EEPROM e carrega os dados da configuração na estrutura wifiConfig.

Finalmente só precisamos usar os valores de wifiConfig para configurar o SSID e senha na função WiFi.begin(), assim como o IP do ESP8266 e IP do Gateway na função WiFi.config().


Continuando a função setup() do ESP8266 vemos os detalhes finais:
  • 1) Apresentamos os dados do roteador em que o ESP8266 conectou.
  • 2) Aqui mostramos a configuração da função de chamada do método GET do formulário de configuração
  • 3) E a configuração da chamada do método POST.
A instrução final do setup() é a chamada da função server.begin() que inicia o servidor. 

É isso pessoal! Baixem o código completo aqui.

Até o próximo artigo!

Abraços,
Renato Aloi

quarta-feira, 1 de junho de 2016

AnalogRead do Arduino no AVR Libc


Veremos nesse artigo como utilizar a conhecida função analogRead() do Arduino no AVR Libc.

No post anterior dessa série, eu falei sobre a Serial usando o AVR Libc, que utilizaremos também nesse post, então se você ainda não estudou esse assunto, leia o outro post primeiro antes de ler esse aqui.

Mas porque e quando devemos utilizar essa técnica? Vejamos, o Arduino nada mais é senão um conjunto de bibliotecas que implementa de "forma amigável" todas as funcionalidades do microcontrolador ATMega da Atmel.

O Arduino utiliza a toolchain da Atmel como base e organiza suas próprias funções em bibliotecas customizadas. A função que veremos nesse artigo é a analogRead() que pode ser encontrada no arquivo wiring_analog.c.

Como sabemos, ao compilar um código do Arduino, acabamos por agregar todas as funções implementadas, inclusive as não utilizadas. Ou seja, ao carregar um programa simples do Arduino que faz piscar um LED, carregamos todas as funções de suas bibliotecas, incluindo de ADC, Serial, Interrupção, Timers etc. Quando queremos um código mais enxuto, utilizamos o AVR Libc e compilamos apenas as bibliotecas que vamos usar, gerando um executável bem menor!

Você pode estudar todos os arquivos utilizados pelo Arduino navegando na pasta Cores que é uma subpasta encontrada no diretório de instalação do Arduino. No Windows 7, devemos encontrar essa pasta no seguinte caminho:

C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino

Lá teremos cabeçalhos como o Arduino.h, wiring.h etc. Nos arquivos de implementação (que nos interessam nesse post), podemos encontrar os comandos de iniciação da parte analógica do Arduino no arquivo wiring.c e a função analogRead() em si, no arquivo wiring_analog.c.

O que é ADC?

ADC é uma sigla em inglês para Conversor Analógico Digital. Quando utilizamos funções como a analogRead() para fazer a leitura de uma porta analógica, convertemos tensão analógica em um valor digital, baseado em limites de referência. Os chips ATMega oferecem algumas soluções de níveis de referência para converter os valores analógicos em digitais.

Para verificar essas opções que temos nos microcontroladores da Atmel, precisamos verificar os registradores que configuram esses circuitos internos de ADC.

Manipularemos dois registradores para configurar o ADC de um ATMega, sendo eles o ADCSRA e o ADMUX. O primeiro é o registrador de controle e status, já o último é o registrador responsável pela configuração do demuxer.

Primeiro precisamos configurar o ADCSRA para ligar o circuito que faz a amostragem da leitura e também ativar esse circuito. Para configurar um registrador, atuamos sobre os bits, ligando ou desligando eles. Vejamos o registrador ADCSRA na imagem abaixo:


Os bits desse registradores são:

  • Bit 7 - ADEN: Ativação do ADC
    Escrevendo o valor 1 (ativando) nesse bit, ativamos o circuito do ADC do ATMega. Escrevendo o valor 0 (desativando), desligamos.
  • Bit 6 - ADSC: Inicia Conversão ADC
    No modo simples de conversão, ativamos esse bit para iniciar cada conversão. No modo Free Running, ativamos esse bit para iniciar a primeira conversão.
  • Bit 5 - ADFR: Seletor do Modo Free Running
    Quando ativado esse bit, ligamos o modo Free Running, em que o circuito de ADC amostra e atualiza os registradores de dados constantemente.
  • Bit 4 - ADIF: Flag de Interrupção
    Quando o circuito ADC completa um ciclo de amostragem e os registradores de dados são atualizados, esse bit é ativado, indicando que o processo terminou. Se o registrador ADIE (e também o bit I do SREG) estiver ativo, o ADIF é desativado automaticamente e a função de interrupção de hardware executada.
  • Bit 3 - ADIE: Ativação da Interrupção do ADC
    Ativando este bit e também o bit I do SREG, ligamos a interrupção do ADC.
  • Bit 2,1,0 - ADPS2, ADPS1, ADPS0: Seletor do ADC Prescaler
    Estes bits determinam o fator de divisão do cristal e o clock do ADC, conforme tabela abaixo:

Para configurar o ADC, vamos utilizar os bits 7, 2, 1 e 0. Vamos utilizar o bit 7 ADEN para ativar o ADC do microcontrolador, mas antes temos que usar os bits ADPS0ADPS1 e ADPS2, conforme tabela acima para configurar a taxa do prescaler, dependendo do cristal que utilizaremos no ATMega.

Precisamos de uma frequência de amostragem entre 50Khz a 200Khz, então pegamos o valor do cristal e dividimos pelo prescaler. O valor do resultado tem que ficar dentro dessa faixa, conforme equação a seguir:

Frequência do Comparador ADC = Frequencia do Cristal / Prescaler

Se estamos usando um cristal de 16Mhz, por exemplo, podemos usar o prescaler de 128 para ter uma frequência de 125Khz, que está dentro da faixa de 50~200Khz. Vamos ver o código como fica:


Criei uma função chamada adc_init() para facilitar, mas ela é bem simples, configurando os bits ADPS2, ADPS1 e ADPS0 com valor de 1 e atribuindo no registrador ADCSRA

A linha depois desliga o modo Free Running, mas essa configuração não é necessária pois esse modo vem desabilitado por padrão.

No código acima utilizamos a Macro _BV() para deslocar os bits do registrador e atribuir apenas os bits a serem configurados. Essa Macro é o equivalente a escrever a instrução (1<<bit).

Portanto na última linha ligamos o bit ADEN para ativar o circuito do ADC. Essa linha poderia ter sido escrita assim:

ADCSRA = ADCSRA | (1<<ADEN)

Usar o operador OR (|) é uma forma segura de atuar apenas no bit que está sendo configurado, pois todos os outros bits do registrador ficarão com suas configurações anteriores. Essa instrução funciona para quando vamos ligar um bit do registrador.

Quando vamos desligar um bit do registrador, utilizamos o operador AND (&) e invertemos todos os bits para desligar apenas aquele bit específico. No caso de desligar o modo Free Running, poderia ser escrita assim a instrução:

ADCSRA = ADCSRA & ~(1<<ADFR)

Veja que usamos o operador TIL (~) para inverter todos os bits.

Depois de configurar o circuito de ADC, podemos criar a função analogRead() configurando o registrador ADMUX e ligando o processo de conversão. 

O registrador ADMUX pode ser visto na tabela abaixo:


Os bits desse registrador são:
  • Bit 7, 6 - REFS1, REFS0 - Seletor da Voltagem de Referência
    Estes bits configuram a referência de voltagem para o cálculo do ADC, conforme mostrado na tabela abaixo:
  • Bit 5 - ADLAR - Resultado Alinhado a Esquerda
    O bit ADLAR afeta na apresentação do resultado. Se este bit for ativado, o resultado será armazenado no registrador alto de dados, ou seja apenas no ADCH. Mas se for desligado o resultado é armazenado alinhado a direita.
  • Bits 3, 2, 1, 0 - MUX3, MUX2, MUX1, MUX0 - Seletor do Canal Analógico
    Estes bits configuram qual será a porta analógica usada para obter a conversão ADC, conforme tabela abaixo:

Precisamos então configurar o valor de referência e a porta analógica a ser utilizada. Vejamos o código e analisaremos em seguida:

Nas primeiras linhas declaramos três variáveis, duas para receber o resultado do comparador ADC, sendo low para o byte menos significativo e high para o byte mais significativo.

A variável bToChannel é uma forma exagerada para garantir que utilizaremos apenas os 3 últimos bits para configurar a porta analógica que vamos utilizar, de ADC0 até ADC7.

Primeiro atribuímos diretamente o valor da variável bToChannel para o registrador ADMUX. Isso vai afetar os bits MUX2, MUX1 e MUX0.

Depois ativamos o bit REFS0, que conforme a tabela acima, configura o ADC para usar a tensão que existe no pino AVCC. Sabemos que esse pino do ATMega é ligado nos 5V, então esse será nosso valor de referência.

A próxima instrução desliga o bit ADLAR para que o alinhamento seja feito a direita e possamos pegar o resultado dos registradores ADCL e ADCH, utilizando todos os 10 bits de resolução, de 0 a 1023. Se ligássemos esse bit ADLAR, o resultado viria apenas no registrador de dados ADCH, com definição de apenas 8 bits, ou seja, de 0 a 255.

Finalmente ativamos o comparador do ADC para iniciar as amostragens, ligando o bit ADSC do registrador ADCSRA. Isso inicia o processo de leitura e amostragem da porta analógica configurada. Quando o circuito ADC termina esse processo ele configura o bit ADIF, portanto usamos a função loop_until_bit_is_set() para ficar em loop até que esse bit seja ativado.

Repare que não utilizamos a configuração de interrupção pelo bit ADIE, pois não existe necessidade. Aqui apenas esperamos terminar o processo e fazemos a leitura dos registradores de controle.

A utilização dessa função pode ser vista no código abaixo:


Repare também que utilizamos a Serial, que aprendemos no outro post, e apenas imprimimos o valor de todas as portas analógicas.

O resultado da execução desse código pode ser visto no Monitor da Serial, conforme imagem abaixo:


O código completo pode ser baixado clicando aqui!

Até o próximo artigo pessoal!