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

domingo, 1 de maio de 2016

Porque o Curso Arduino Não Publica Mais Vídeos Como Antigamente



Pessoal, bom dia!

Resolvi escrever esse post para explicar porque não publico mais vídeos do Curso Arduino (como publicava em 2011), no canal do Youtube: http://www.youtube.com/cursoarduino

O que acontece na prática é que dá muito trabalho gravar aulas, editar, compilar e publicar todo material de uma aula. Leva muito tempo e muita dedicação.

Em 2011, no Brasil, tínhamos uma economia estável e, na época, eu criei uma loja de componentes eletrônicos para sustentar o trabalho que fazia com as aulas do Curso Arduino.

A loja funcionou bem (na medida do possível, pois eu nunca fui bom lojista) até o segundo semestre de 2014. Foi nessa época o dólar subiu de mais ou menos R$ 2,00 para R$ 4,00, dobrando de valor.

Essa alta do dólar quebrou a Loja do Curso Arduino instantaneamente!

Tentei manter a loja funcionando até o início de 2015, mas como tive que tirar dinheiro de algum lugar para comprar Arduinos e Módulos pelo dobro do preço, para atender os clientes que já haviam comprado pelo valor antigo, acabei me endividando com os Correios. Sem os Correios, não tinha como enviar mais as encomendas. Aí tive que fechar de vez a loja.

Desde então tive que voltar a fazer projetos para clientes da indústria. E tenho que visitar clientes por todo o Brasil. Desmontei meu laboratório e coloquei o essencial em algumas caixas para levar nos clientes. Então agora não tenho mais um espaço dedicado para fazer as aulas. Por isso para mim agora é tão difícil gravar aulas, responder dúvidas e publicar material do Curso Arduino nas mídias sociais da Internet.

Estou esperando a economia estabilizar novamente e também estou fechando parcerias com lojas e fabricantes de módulos para poder voltar a sobreviver. Mas como está tudo tão instável no Brasil, continuo esperando por tempos melhores.

Abraços,
Renato Aloi