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!

segunda-feira, 16 de maio de 2016

Serial do Arduino Usando AVR Libc


Este artigo vai para quem utiliza as placas do Arduino para desenvolvimento de hardware, mas que não gosta muito são das bibliotecas do Arduino. Pois elas oferecem apenas encapsulamentos sobre as funções do AVR Libc, ou então apenas reescrevem as funções que a Libc já provém.

Comunicação Serial é um bom exemplo disso. O Arduino fornece sua própria implementação de funções como Serial.print(), Serial.println() e Serial.read(). Ao mesmo tempo a AVR Libc já provê funções como printf(), puts() e getchar(). Este artigo explica quão fácil é implementar essas funções da Libc na comunicação Serial da plataforma megaAVR da Atmel.
Se você não possui muita experiência em programação é melhor provavelmente você continuar usando as bibliotecas do Arduino. Elas são boas em esconder as características mais complicadas da programação embarcada. Entretanto você deve migrar enquanto amadurece no desenvolvimento dos seus projetos. Os datasheets da Atmel não são tão complicados como parecem de início. E você deve estudar também o código pronto deste artigo.

Configurando UART

Microcontroladores AVR possuem três registradores de controle e de status. O registrador UCSRA normalmente contém dos dados de status. Já os registradores UCSRB e UCSRC contém os conjuntos de configurações. Veja as tabelas no fim deste artigo para conhecer todos os possíveis valores.
A AVR Libc fornece macros auxiliares para cálculos de ‘baud rate’, incluindo em um arquivo de cabeçalho chamado ‘setbaud.h’, que precisa que as macros F_CPU e BAUD sejam definidas. Ao incluir esse arquivo de cabeçalho, as macros UBRRL_VALUE, UBRRL_VALUE e USE_2X são definidas. As duas primeiras são usadas para configurar a velocidade da Serial. A última é usada para determinar se a Serial deve ser configurada para rodar no dobro da velocidade, dado um determinado baud rate.

UCSZ2, UCSZ1 e UCSZ0 são registradores que controlam o tamanho dos dados. São possíveis as configurações de 5-bit (000), 6-bit (001), 7-bit (010), 8-bit (011) e 9-bit (111). A mais comum é o tamanho de 8-bit. O formato mais conhecido e utilizado de comunicação Serial é o 8N1, ou seja, 8-bit, sem paridade e com 1-bit de parada. O que configura o bit de parada para 1-bit é o registrador USBS. E os registradores que configuram o modo de paridade são os UPM1 e UPM0. Para o modo 8N1 basta não configurar nada nesses registradores, ou seja, ambos precisam ser zero para desativar a paridade.

Utilizando os bits ensinados acima, podemos então configurar a Serial:

#define F_CPU 16000000UL
#define BAUD 9600

#include <util/setbaud.h>

void uart_init(void) {
    UBRRH = UBRRH_VALUE;
    UBRRL = UBRRL_VALUE;
#if USE_2X
    UCSRA |= _BV(U2X);
#else
    UCSRA &= ~(_BV(U2X));
#endif
    UCSRC = _BV(URSEL) | _BV(USBS) | _BV(UCSZ1) | _BV(UCSZ0);  // format 8N1
    UCSRB = _BV(RXEN) | _BV(TXEN);   /* Enable RX and TX */
}

Lendo e Escrevendo na Serial

Você pode transmitir dados pela Serial escrevendo bytes no registrador de dados UDR. Primeiro você deve certificar que a Serial está pronta para transmitir novos dados (ou seja, que já transmitiu os dados anteriores).

Aqui você tem duas opções, esperar que o registrador de dados vazios UDRE seja ativado, ou você pode, após o envio de cada byte, esperar a confirmação da transmissão, através do registrador de transmissão completada TXC, que é ativado quando a transmissão foi concluída.

Opção 1, através do registrador UDRE:

void uart_putchar(char c) {
    loop_until_bit_is_set(UCSRA, UDRE); /* Wait until data register empty. */
    UDR = c;
}

Opção 2, através do registrador TXC:

void uart_putchar(char c) {
    UDR = c;
    loop_until_bit_is_set(UCSRA, TXC); /* Wait until transmission ready. */
}

Para receber dados pela Serial, você pode ler bytes do registrador de dados UDR. O processo de recebimento concluído é indicado pelo registrador RXC que é ativado quando existe dados disponíveis na serial.

char uart_getchar(void) {
    loop_until_bit_is_set(UCSRA, RXC); /* Wait until data exists. */
    return UDR;
}

Redirecionando Fluxo de Dados Para a Serial

Existe uma macro chamada FDEV_SETUP_STREAM que pode ser usada para configurar um buffer que é válido para operações de entrada/saída padrão. O buffer iniciado deve ser do tipo FILE. Você pode definir buffers separados para entrada e para saída. Ou você pode definir um único buffer que funciona para ambos, tanto para entrada como para saída. O primeiro e segundo parâmetro da função recebem os nomes das funções a serem chamadas quando data está para ser lida ou escrita no buffer.

Utilizando um buffer separado para cada operação:

FILE uart_output = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

FILE uart_input = FDEV_SETUP_STREAM(NULL, uart_getchar, _FDEV_SETUP_READ);

Utilizando um buffer único para entrada e para saída:

FILE uart_io = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);

Para preparar as nossas funções uart_putchar e uart_getchar para serem usadas com os fluxos de entrada/saída, temos que mudar as definições um pouco.

void uart_putchar(char c, FILE *stream) {
    loop_until_bit_is_set(UCSRA, UDRE);
    UDR = c;
}

char uart_getchar(FILE *stream) {
    loop_until_bit_is_set(UCSRA, RXC); /* Wait until data exists. */
    return UDR;
}

Agora podemos redirecionar ambos STDIN e STDOUT para a Serial. Isso nos habilita a usar as funções de leitura e escrita da AVR Libc.

int main(void) {
    uart_init();
    stdout = &uart_output;
    stdin  = &uart_input;
    char input;
    while(1) {
        puts("Hello world!");
        input = getchar();
        printf("You wrote %c\n", input);
    }
    return 0;
}

Registradores de Controle e Status

UCSRA Bit #
Nome
Descrição
bit 7
RXC
USART Recebimento Completo. Ligado quando tem dados disponíveis no registrador de dados que não foram lidos ainda.
bit 6
TXC
USART Transmissão Completa. Ligado quando todos os dados foram transmitidos.
bit 5
UDRE
USART Registro de Dados Vazio. Ligado quando o registrador UDR está vazio e novos dados podem ser transmitidos.
bit 4
FE
Erro de Frame. Ligado quando próximo byte no registrador de dados UDR possui erro de frame.
bit 3
DOR
Dados Atropelados. Ligado quando o registrador UDR não foi lido antes do próximo frame ter chegado.
bit 2
PE
USART Erro de Paridade. Ligado quando o próximo frame no registrador UDR possui erro de paridade.
bit 1
U2X
USART Velocidade Dupla de Transmissão. Quando ligado diminui o tempo de bit pelo dobro da velocidade.
bit 0
MPCM
Modo de Comunicação Multi-processamento. Quando ligado os dados recebidos são ignorados se não possuírem informação de endereço.

UCSRB Bit #
Nome
Descrição
bit 7
RXCIE
RX Ativação da Interrupção de Conclusão. Ligue para permitir interrupção quando recebimento completo.
bit 6
TXCIE
TX Ativação da Interrupção de Conclusão. Ligue para permitir interrupção quando transmissão completa.
bit 5
UDRIE
USART Registrador de Dados Vazio com Interrupção Ativa. Ligue para permitir interrupção quando registrador de dados vazio.
bit 4
RXEN
Recebimento Ativo. Ligue para ativar o recebimento.
bit 3
TXEN
Transmissão Ativa. Ligue para ativar a transmissão.
bit 2
UCSZ2
USART Tamanho do Caractere. Utilize em conjunto com UCSZ1 e UCSZ0 para configurar o tamanho do frame. Tamanhos disponíveis são 5-bit (000), 6-bit (001), 7-bit (010), 8-bit (011) e 9-bit (111).
bit 1
RXB8
Receptor do Bit 8 de Dados. Quando usando modo de 9-bit, este registrador recebe o nono bit do recebimento do frame.
bit 0
TXB8
Transmissor do Bit 8 de Dados. Quando usando modo de 9-bit, este registrador envia o nono bit da transmissão do frame.

UCSRC Bit #
Nome
Descrição
bit 7
URSEL
Seletor de Registrador. Ative esse registro quando escrevendo no registrador UCSRC, pois esse registrador é compartilhado com o UBRRH.
bit 6
UMSEL
USART Seleção de Modo. O bit seleciona entre modos (0) assíncrono ou (1) síncrono.
bit 5
bit 4
UPM1
UPM0
USART Modo de Paridade. UPM1:0 selecionam o modo de paridade, sendo (00) nenhum, (10) par (11) ímpar.
bit 3
USBS
USART Seletor do Bit de Parada. Escolhe a quantidade de bits de parada, (0) 1 bit e (1) 2 bits.
bit 2
bit 1
UCSZ1
UCSZ0
USART Tamanho do Caractere. Utilize em conjunto com UCSZ2 para configurar o tamanho do frame. Tamanhos disponíveis são 5-bit (000), 6-bit (001), 7-bit (010), 8-bit (011) e 9-bit (111).
bit 0
UCPOL
USART Polaridade de Clock. Utilizado apenas em modo síncrono, sendo (0) borda de subida na transmissão e descida na recepção e (1) borda de descida na transmissão e subida na recepção.

Dúvidas como programar Arduino no AVR Studio? Veja as aulas abaixo:

Curso Arduino Express Aula 5 - Arduino vs. Atmel Studio 6, Parte 1/2 (seriallink.com.br)



Curso Arduino Express Aula 5 - Arduino vs. Atmel Studio 6, Parte 2/2 (seriallink.com.br)


sexta-feira, 13 de maio de 2016

Tratando POST no Web Server do ESP8266


Nesse post vamos continuar o assunto do post anterior (se você ainda não leu clique aqui) e, aproveitando o mesmo código, vamos implementar o tratamento POST de envio de formulário.

Para quem não conhece bem HTML e seus meandros, isso significa que vamos criar um formulário conforme a imagem abaixo e enviar os dados preenchidos para serem tratados por uma função e apresentados em uma outra tela.


Podemos ver no formulário que temos um campo de texto e um botão enviar. É um exemplo bem simples, mas mostra o potencial do ESP8266, utilizando a biblioteca WebServer.

Repare que diferente da página principal, esse formulário chamamos no endereço http://192.168.0.125/form

Lembra do código do post anterior que eu expliquei como o ESP8266 trata as URLs? Vejamos novamente:

Através da função server.on() dizemos que ao chamar a URL raiz, ou seja http://192.168.0.125/, o código executado é o da função handleRoot().



Pois bem, precisamos agora acrescentar um tratamento para a URL http://192.168.0.125/form, então vamos criar outra função server.on(), mas agora vamos definir também o método que a URL vai tratar.

O que significa o método da URL? Vamos dizer que existem formas diferentes de se interagir com as URLs. Não quero entrar nos detalhes do protocolo HTTP que regimenta essa parte, mas de forma resumida, quando chamamos uma URL, digitando no Navegador de Internet, estamos executando essencialmente um método chamado GET. Agora, quando utilizamos um formulário, temos a possibilidade de instruir o Navegador de Internet, via código, de fazer uma chamada pelo método POST.

Ambos métodos GET e POST operam de forma similar e aceitam passagem de parâmetros entre uma chamada e outra. Essa é a forma que o HTML envia dados de uma página para outra. Isso acontece pois o protocolo HTTP, que é o responsável por enviar HTML de um lado para outro, não guarda estado das coisas, não armazenando nada. Então as informações precisam ser enviadas e recebidas como se fossem um anexo de um e-mail (veremos como enviar e receber e-mail pelo ESP8266 em outro post).

Para nosso exemplo, vamos considerar que o método GET será tratado pela função formularioChamada() e que o POST será tratado pela função formularioEnviado(), então precisamos escrever o seguinte código:


Repare que utilizamos duas vezes a função server.on() para a mesma URL "/form", mas no segundo argumento diferenciamos as chamadas pelos métodos. Para isso utilizamos as macros HTTP_GET e HTTP_POST. E no terceiro parâmetro passamos a função que deve ser executada.

A função formularioChamada() se parece bastante com a handleRoot(), que apresenta a página inicial, mas dessa vez adicionamos uma Tag Form para montar e configurar um formulário com método POST de envio.


Os pontos importantes desse código são o método que foi configurado para  POST e o 'action' que foi configurado para '/form'. Para todos os efeitos a URL é a mesma, mas como o método é diferente, e quando o usuário clicar no botão Enviar, que é montado pela tag Input com o tipo Submit, a função que será chamada será a formularioEnviado(), pois configuramos isso mais acima, na função server.on().

Vamos ver agora alguns detalhes da função formularioEnviado(), que não vou colocar na integra aqui, pois utiliza os mesmos princípios das outras, mas o que muda é que adicionamos um tratamento para manipular os parâmetros que chegaram do envio do formulário.


O interessante da biblioteca ESP8266WebServer.h que utilizamos aqui é que ela traz vários métodos auxiliares para tratarmos parâmetros passados entre uma página HTML e outra. Vejamos alguns:

  • String arg(String name); -- recupera o valor do campo pelo nome do parâmetro
  • String arg(int i); -- recupera o valor do campo pelo índice do parâmetro
  • String argName(int i); -- recupera o nome do campo pelo índice do parâmetro
  • int args(); -- recupera a quantidade de parâmetros
  • bool hasArg(String name); -- verifica se o parâmetro existe

No código acima temos:

  1. 1) Chamada da função server.hasArg() com o nome do campo de texto para saber se existe esse parâmetro
  2. 2) Chamada da função server.arg() para efetivamente recuperar o valor que foi digitado no campo de texto
  3. 3) Repare que criamos outro formulário, mas dessa vez com o método GET, para adicionar um botão de 'Voltar', que ao ser clicado vai chamar a URL '/form', mas com o método GET, fazendo com que a função formularioChamada() seja evocada, apresentando novamente a tela anterior, ou seja, a tela do formulário. Tudo isso é regimentado pela configuração que fizemos na função server.on()
O resultado final deve se parecer com o da imagem abaixo. Eu digitei "teste de envio" na caixa de texto, e esse valor foi concatenado conforme vimos no código acima e apresentado no HTML de resposta ao form, ou seja, o HTML que trata o POST.


É isso aí pessoal! Experimentem outros tipos de campos do HTML, como checkboxes, listas de seleção etc, e também outros métodos de tratamentos dos campos, como o server.args(), que retorna a quantidade de campos enviados pelo formulário. Experimente esse método e veja que no nosso formulário existem 2 campos, pois o botão é considerado um dos parâmetros passados pelo POST de uma página para outra.

Baixe o código completo clicando aqui!

Até o próximo artigo da série!

Abraços,
Renato Aloi

terça-feira, 10 de maio de 2016

Código Básico do Web Server no ESP8266



Pessoal,

Vou lançar uma série de posts sobre o ESP8266. Se você ainda está perdido no assunto, veja esse vídeo aqui e a segunda parte aqui.

Depois de assistir esses dois vídeos você estará apto a montar o circuito do ESP8266 e também já saberá como configurar o Arduino IDE para subir a programação.

Agora vamos falar da programação básica do ESP8266. Nesse primeiro post eu vou mostrar o código básico de um Web Server.

Para quem não sabe o que é um Web Server, imagine páginas de um site que você gosta. Essas páginas que acessamos, por exemplo do Facebook, estão hospedadas em algum lugar. Esse lugar chama Web Server, ou seja "Servidor Web".

No caso do Facebook, as páginas ficam hospedadas em um servidor gigantesco. Já no nosso caso o ESP8266 pode se conectar em uma rede sem fio (WiFi) e operar como um Servidor Web pequeno.

Para isso basta usar o código básico que vamos ver agora e configurar os dados da rede sem fio. É bem simples, vamos ver a primeira parte do código:

No código acima vemos alguns includes, algumas declarações e uma função chamada "handleRoot".

Nas duas primeiras linhas temos os includes das bibliotecas necessárias que são a "ESP8266WiFi.h" com funções para auxiliar na conexão da rede sem fio. E temos também a biblioteca "ESP8266WebServer.h" que possui as funções para hospedar páginas Web.

Na terceira linha, temos a declaração do ESP8266WebServer e entre parêntesis a porta 80 que é a padrão da Web.

A partir da quarta linha temos uma função chamada handleRoot() que será executada toda vez que o endereço raiz do ESP8266 for acessado pelo navegador. Veremos como o ESP8266 sabe que precisa chamar essa função, em breve.

Dentro dessa função temos a declaração de uma variável do tipo "String" chamada "html". Nessa string colocamos todo nosso código HTML que será exibido ao carregarmos a página inicial do ESP8266.

Depois de preencher todo valor do HTML na variável, chamamos a função server.send(), passando os parâmetros de código de retorno (200 significa retorno OK), o tipo do retorno (text/html significa que retornaremos dados do tipo texto na formatação HTML), e a variável "html" que contém o conteúdo da página.

Pronto, isso é o necessário para responder a uma chamada de página principal quando acessamos o ESP8266 no endereço raiz.

Vamos ver agora como configuramos a rede e as configurações para isso funcionar.

Na imagem acima podemos ver que nesse código também temos a função setup(), que é bem conhecida para quem programa em Arduino. Afinal de contas esse é um código Arduino, que será compilado e carregado pelo Arduino IDE, então isso facilita bastante as coisas pra quem já programa nessa linguagem.

Na função setup() declaramos o inicio da Serial através da função Serial.begin(), com o valor 9600 que é um valor comum de velocidade da Serial. Você pode usar aqui outras velocidades como 115200 etc.

Igualmente precisamos chamar a função WiFi.begin() para iniciar a rede sem fio do ESP8266, mas nesse caso passamos duas configurações que são: o "nome da rede" e a "senha". Esses dados são da rede que vamos conectar, então normalmente são do roteador ou ponto de acesso onde conectamos nossos computadores. É importante que o ESP8266 esteja na mesma rede que os computadores que vão acessá-lo.

A partir desse ponto, temos configurações de IP, que são meio chatas, mas necessárias para sabermos como acessar nosso WebServer do ESP8266. É possível fazer com que a rede atribua automaticamente o IP e também é possível usar um nome pra facilitar o acesso, mas essa última opção ainda não consegui fazer funcionar.

A opção do IP automático pelo DHCP da rede eu não costumo usar pois senão não sabemos qual IP o ESP8266 pegou.

Na primeira linha após a função WiFi.begin() temos a declaração de uma variável IP, utilizada como máscara de rede, chamada subnet. Na linha seguinte temos a chamada da função WiFi.config(), que recebe 3 parâmetros, sendo o IP do ESP8266, o IP do roteador e o IP da subnet.

Nos dois primeiros parâmetros eu crio o IP na mesma linha para, e para a máscara de rede, que é o terceiro parâmetro, eu utilizo a variável subnet, criada na linha acima.

O código a seguir é um loop verificando o status da rede para saber se o ESP8266 já conseguiu conectar. Então é utilizada a função WiFi.status() na condição de um while() para verificar o status de conexão. Se for WL_CONNECTED, significa que conseguiu conectar e o código sai do while() para continuar. Se o roteador não estiver ao alcance da rede, o código vai ficar preso eternamente nesse ponto. E isso gasta muita energia. Veremos como contornar isso em outro post.

Veremos também (em outro post) como configurar esses parâmetros de nome da rede, senha e IP's sem que sejam fixos no código.

Enquanto o ESP8266 tenta conectar imprimimos na serial um ponto, indicando um andamento, como se fosse uma barra de progresso, através da função Serial.print('.');

Depois que o código passou pelo while(), imprimimos o IP que o ESP8266 conectou na rede chamando a função WiFi.localIP().

Essa é uma técnica que usamos para saber qual o IP que o ESP8266 conectou, quando usamos a atribuição de IP automática. Mas essa técnica só funciona quando estamos em desenvolvimento com o ESP8266 conectado no computador através da USB. Depois quando colocamos o ESP8266 em produção, instalando na automação da casa, não temos mais essa informação. Por isso prefiro usar IP estático.

Na linha seguinte temos a configuração que define qual URL vai acionar qual Função. No caso, temos apenas a URL raiz, identificada pela barra invertida "/" e a função "handleRoot". Utilizamos a função server.on() para configurar isso. Veja que é bem simples e você pode criar outras funções para qualquer endereço que você quiser.

Por exemplo, vamos dizer que você quer criar uma URL de saída chamando a função "funcSair", você faria assim:

server.on("/sair", funcSair);

E depois lógico você precisa criar a função funcSair() conforme fizemos com a handleRoot() e isso precisa ser antes do setup().

Finalmente utilizamos a função server.begin() para iniciar o servidor Web e depois avisamos na Serial que o servidor HTTP foi iniciado.

Servidor HTTP é um outro nome que damos para o Servidor Web, ou Web Server. É tudo a mesma coisa, embora o Servidor Web possa também acumular tarefas de outros protocolos além do HTTP, como FTP, SMTP etc. Veremos isso em outra hora.

Para terminar temos a função loop() que tem apenas uma única instrução.

A função server.handleClient() fica eternamente lidando com a conexão do cliente. Nesse cenário, o Servidor é o ESP8266 e o cliente é o Navegador de Internet que utilizamos para acessar as páginas HTML do ESP8266.

Baixe o código completo clicando aqui!

Até o próximo artigo!

Abraços,
Renato