Manual

do

Maker

.

com

ESP8266 com PIC

ESP8266 com PIC

Nesse post disponho um exemplo de comunicação entre uma controladora PIC e um módulo WiFi ESP8266, trocando informações com uma aplicação remota (feita em Qt) através de comunicação socket. A programação do PIC está sendo feita através da MikroC IDE, que permite fazer a programação da MCU em linguagem C, além de possuir bibliotecas para muitas funções, tal qual em Arduino.

Utilizo mais uma vez o módulo ESP8266-01, citado anteriormente nesse outro post, onde foi utilizado um buffer não inversor CD4050 para baixar a tensão de 5v para 3.3v na comunicação entre o Arduino e o ESP8266. Dessa vez não será necessário utilizar o buffer porque a microcontroladora PIC utilizada é a P16F883, que trabalha de 2v a 5.5v, então, utilizaremos diretamente 3.3v para a alimentação de ambos os componentes.

Utilizarei aqui o sensor de linha, descrito nesse post para exemplificar a coleta e emissão de dados através da comunicação via socket.

Ao final do artigo, disponibilizarei os links para download dos códigos de exemplo, portanto, sinta-se livre para desfrutar da leitura.

PIC 16F883

Escolhi essa MCU porque das pequenas que tenho é a que me oferece um recurso indispensável; UART port hardware. Do mesmo modo que com Arduino, é possível utilizar o recurso de softserial, mas em altas velocidades começa a gerar ruído e isso é uma das coisas que não deve acontecer nessa comunicação. Além disso, a MCU será configurada para utilizar o oscilador interno (que é outro recurso que considero indispensável para simplificação da prototipagem e economia de energia) em 8MHz, podendo ser ajustado até 32KHz, mas testes com a UART deverão ser executados para tal. Rodando em 32kHz a 2v, a corrente é de 11uA, mas não seria possível utilizar a UART À 38400 kbauds. É uma MCU extremamente econômica; utilizando qualquer timer que não o TMR0, pode-se fazê-la dormir e levantar em uma interrupção, porém não será necessário esse extremo. Em outros posts sobre PIC veremos como espremê-lo até ligá-lo a um limão :-)

Repare no desenho que cada pino possui diversas funcionalidades. Para utilizar uma delas especificamente, deve-se configurar a MCU previamente. O clock também é configurado por software, por isso costumo criar uma função setup() como a do Arduino para inicializar as configurações previamente (mas nada que não pudesse ser feito diretamente em main() - e assim será). Não vou colocar todo o código no post senão ficará enorme, mas vou citar as partes que acho importante.

Para iniciar, crie um projeto novo na IDE MikroC, selecionando o modelo da MCU (PIC16F883) e clock interno a 8MHz na configuração de bits. Ainda na configuração de bits, desabilite o Brown-Out, que é um fusível para reiniciar o PIC quando ele estiver abaixo de 4.5v (isso também é configurável, mas desse modo já basta). Após isso, uma função setup contendo as configurações descritas:

Desabilitar comparadores

void setup(){
     C1ON_bit = 0;
     C2ON_bit = 0;

Configuração da leitura analógica

O pino analógico 0 será utilizado para fazer a leitura do sensor de linha, os demais pinos serão configurados como digital:

     
     //Tratamento dos pinos Analogicos
     ANSEL  = 0x01;              // RA0 como entrada analógica
     ANSELH = 0;                 // Demais pinos, digital

Inicialização do módulo AD

Uma vez que a leitura analógica será utilizada, deve-se iniciar o módulo AD:

     //Inicializacao do modulo AD
     ADC_Init();

Configuração de 1NPUT 0UTPUT dos pinos

Em PIC define-se em TRISx se o pino será input ou output. Para fácil assimilação, pense em 1nput e 0utput. Colocando 0xFF, todos os bits são setados como 1nput no TRIS da porta A e C, como exemplificado abaixo:

    //TRIS
    TRISA = 0xFF; //TUDO INPUT nas portas A
    TRISC = 0xFF; //o mesmo para portas C

Habilitar interrupções

A interrupção será habilitada apenas para fazer a leitura de RX. Há como fazê-lo sem interrupção, uma vez que todo o código seguirá um fluxo e nesse caso, a interrupção poderia ser dispensada. Mas é válida como exemplo porque poderia haver um caso de interrupção externa gerada por um sensor digital, por exemplo. Assim que tiver uma oportunidade, escrevo algo a respeito, mas inicialmente um bom exemplo sobre interrupções pode ser visto aqui, ou aqui, também aqui, mais um aqui e um que gosto bastante, bem aqui.

Basicamente, será habilitada a interrupção externa no RX, desabilitada no TX e periféricamente. Por fim, a chave geral das interrupções é ligada.

    
    //interrupcoes
    //               RCIE___./ .___PEIE___./ .___GIE
    RCIE_bit    = 1;         // habilitar interrupcao  em RX
    TXIE_bit    = 0;         // desabilita interrupcao em TX (default)
    PEIE_bit    = 1;         // habilitar/desabilitar interrupcoes perifericas
    GIE_bit     = 1;         // liga chave geral das interrupcoes
    TMR0IE_bit = 1;         //interrupcao do timer0
    

Configurar frequência do oscilador interno

As configurações não são feitas "de cabeça". Obviamente eu li o datasheet para executá-las, ainda mais que programo um monte de modelos de PIC, então não quero e não vejo razão para decorar tudo, uma vez que está documentado, bastando saber o quê procurar na documentação. Para configurar o oscilador interno a 8MHz, o seguinte conjunto de bits deve ser disposto no registrador OSCCON:

 
    //oscilador interno a 8MHz
    OSCCON = 0b01110101;

Esse tunning pode ser necessário dependendo da frequência selecionada (qualquer coisa que não 8MHz). O datasheet descreve de forma sublime a configuração interna do oscilador interno.

    
    //Tunning nao necessario porque esse PIC ja vem calibrado a 8MHz
    //OSCTUNE = 0b00001111;

Poderia sem dúvida alguma ser feita no loop principal porque o código
não foi escrito para ser multithread, mas não por isso deixarei de me divertir um pouco com os recursos
da controladora. Abaixo, a configuração do timer0, descrito nesse post.

    
    //=-=-=-=-=-=-=-=-=-= TMR0 =-=-=-=-=-=-=-=-=-=-=-=-=
    /* Overflow:
      Fosc = Fcy/4
      Fovr = Fosc/(4*PRESCALER*RESOLUTION_TIMER(*POST))
      
      Comparador:
      Fccpif = Fosc/(4*COMPARATOR*PRE)
    */
    //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 /* =-=-=-=-= Calculando o valor do timer =-=-=-=-=-=
    ciclo de maquina = Fosc/4 para o oscilador interno
    Tempo (t) do overflow:
    t = ciclo * prescaler * TMR0
    O clock esta configurado para 8Mhz (8000000)
       
    ciclo = 8/4 = 2us
    prescaler = 1:2 (2)
    t = 2 * 2 * 256 (TMR0 guarda de 0 a 255 antes do overflow porque ele tem 8 bits)
    t = 1ms
    Desse modo, a piscada eh a cada 1 segundo. Com prescaler a 1:4, dobra o tempo, logo,
    blink de 1 segundo.

    //RBPU   1 - pullup disabled na portb
    //INTEDG 0 - detecta interrupcao em LOW
    //T0CS   0 - Fosc/4 (clock interno)
    //T0SE   0 - incrementa da baixa pra alta
    //PSA    0 - prescaler enabled
    //PS2,PS1,PS0 010 - 1:4 no prescaler do TMR0
    */
    OPTION_REG = 0b10000010;


Inicialização do UART por hardware

Tal qual em Arduino, devemos inicializar a UART, mas como estamos utilizando uma MCU virgem, sem bootloader nem cristal, será necessário verificar o baud rate suportado conforme o clock utilizado. O cálculo do baud rate foi exemplificado nesse outro post.

    
    //Inicializa UART
    UART1_Init(38400);
    Delay_ms(100);

O ESP8266 já está previamente configurado para entrar na rede, portanto a única preocupação desse programa é garantir que seja possível trocar mensagens com o servidor através do ESP8266. Para tal, simplesmente valida-se as configurações de múltiplas conexões e a comunicação TCP estabelecida com o socket remoto. Simplesmente estou enviando os comandos AT,CIPMUX,TCPSTART e CIPSEND. Consegui deixar espaço o suficiente na memória para processar tratamento de erros e tomada de decisão, mas não vou implementar agora porque tenho vários outros códigos pra por a mão e no momento me basta que isso funcione.

Ainda que o servidor (que recebe a conexão socket) caia ou seja reiniciado, do modo burro que o programa está operando agora, automaticamente se reconectará e em algum momento dentro de 30 segundos já estará devidamente sincronizado com a aplicação. Criei uma função que chama outras, sendo essa a rotina:

    
void sendSample(){
    //1 - esp8266 responde?
    atStatus();
    //Forca cipmux, nao importa se ja esta
    cipmux();
    //estabelece a conexao TCP remota
    tcpStart();
    //envia a mensagem
    sender();
    //envia sem verificao, como todas funcoes anteriores
    sensorStatus();
}

Voltando ao início do código, algumas definições foram criadas, relacionadas às configurações de rede. O ID do dispositivo, IP do servidor, porta de conexão remota e ID da conexão TCP com o ESP8266.

A diretiva DEFINE não ocupará memória do programa, ele é tratado previamente à compilação. Essa diretiva é útil para quando se quer ter uma visão clara de uma função ou para não ter que decorar valores, ou para digitar pouco, etc.

//PIC16F690
//=-=-=-=-=-= DEFINES =-=-=-=-=-=
#define ON 0
#define OFF 1
#define MSGLEN 19
#define OK 1
#define DONE 0
#define MYID "1"
#define TCPID "4"
#define SERVER "192.168.1.232"
#define PORT "9000"
#define PROTOCOL "TCP"
#define LOW 0
#define HIGH 1
#define TIMEOUT 10
#define YES 1
#define NO 0
#define UDELAY 20
#define DELAY 100
#define UART_DELAY 400
#define CIPSIZE 3
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Diversas variáveis estão acessíveis de forma global:

//=-=-=-=-=-=-=-=-=-=-=-= int =-=-=-=-=-=-=-=-=-=-=-=-=-=//
//contador de uso geral                                  //
int count           = 0;                                 //
//acumulador de overflows do timer0, ate 1seg (1000ms).  //
int oneSecond       = 0;                                 //
//media de 3 amostragens do sensor                       //
int sensorSample    = 0;                                 //
int     rxRead      = 0;                                 //
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=//

//-=-=-=-=-=-=-=-=-=-=-=-= char =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=//
// 19 bytes. triste, mas inevitavel por causa da mensagem de conexao TCP //
char text[MSGLEN]   = {0};                                               //
//tamanho da mensagem de envio. Ex.: esp8266:15,1                        //
char cipsendSize[CIPSIZE] = {0};                                         //
//acumulador de segundos. trabalha em conjunto com o oneSecond           //
char secs      = 0;                                                      //
//guarda a palavra a encontrar na resposta do reader()                   //
char word = 0;                                                           //
//guarda o valor do sensor                                               //
char sensorVal = 0;                                                      //
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=//

Costumo fazer um rascunho da MCU utilizada para me guiar durante a codificação:


 //=-=-=-=-=-=-= PIC16F690 =-=-=-=-=-=-=-=
 /*


//mapeamento dos pinos utilizados
//=-=-=-=-=-=-= PIC16F883 =-=-=-=-=-=-=-=
 /*
          -|1  U  28|-
          -|2     27|- RB6 READER (flag)
          -|3     26|- RB5 (Sensor - Analogico 13)
          -|4     25|-
          -|5     24|-
          -|6     23|-
          -|7     22|-
      VSS -|8     21|-
          -|9     20|- VDD
          -|10    19|- VSS
RESET RC0 -|11    18|- RC7 (RX)
      RC1 -|12    17|- RC6 (TX)
  DBG RC2 -|13    16|-
  LED RC3 -|14    15|- RC4
 */
 //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Aliases (ou "apelidos") para as portas e pinos são bastante úteis para não ter que decorar os nomes dos respectivos bits, assim como são facilitadores para trocar os pinos em questão, bastando alterá-lo na definição do alias e não sendo necessário tocar no restante do código.


//=-=-=-=-=-=-=-= ALIASES =-=-=-=-=-=-=-=
sbit RX_port     at RC7_bit; //RX
sbit TX_port     at RC6_bit; //TX

//TESTE de delay, conexao etc.
sbit TRIS_LED    at TRISC3_bit;
sbit LED_port    at RC3_bit;

//debugger temporario
sbit TRIS_DBG    at TRISC2_bit;
sbit DBG_port    at RC2_bit;

// hard reset
sbit TRIS_RESET  at TRISC0_bit;
sbit RESET_port  at RC0_bit;

//flag para leitura da uart
sbit TRIS_READER at TRISB6_bit;
sbit READER_port at RB6_bit;

//SENSOR  ANALOGICO
sbit TRIS_SENSOR at TRISB5_bit;
sbit SENSOR_port at RB5_bit;

//UART
sbit TRIS_RX     at TRISC7_bit;
sbit TRIS_TX     at TRISC6_bit;
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Na função main(), alguns ajustes mais relacionados aos TRIS e PORT podem ser vistos em detalhes baixando o código nesse link.

Assim, estão finalizadas as configurações iniciais. Agora podemos partir para o código que fará a leitura do sensor e o envio através do ESP8266.

Limpar variáveis
Pode parecer preciosismo e talvez até seja, mas deixe-me iniciar com minhas considerações:

  • Não existe string em C, existe array de chars.
  • Se utilizarmos sizeof(var) teremos o tamanho de alocação da variável em memória.
  • Se utilizarmos strlen(var) teremos o valor utilizado do array.

Pode-se então utilizar strncpy em conjunção a um outro recurso da linguagem e preencher o buffer, mas não existe mágica, de alguma forma haverá uma interação de uma dessas funções junto com a sua. Como eu sei o tamanho do array, prefiro ir diretamente ao ponto, fazendo um loop e preenchendo o array de char com 0 (porque '\0' é o 0 literal). Por isso crio essa função sempre:

void clear(char *var,short int size){
    for (i=0;i<size;i++){
        var[i] = 0;
    }
}

O ESP8266 responde?

A primeira etapa da comunicação entre a MCU e o ESP8266 é o status serial através do comando 'AT', sem parâmetros. Observe que o ESP826 espera CR (carriege return) e LF (Line Feed), portanto devemos somar os bytes da mensagem excluindo "rn", mas não deixando de enviá-los.

Como citei anteriormente, fiz questão de colocar pra funcionar primeiro e os tratamentos de exceções serão feitos posteriormente, mas essa função é a primeira necessária para a validação.

void atStatus(){
    word = 'O';
    //A funcao reader() se encarrega de fazer o tratamento da resposta
    write("ATrn");
}

A função write() é um auxiliador na escrita, nada muito significativo. A variável word é uma flag; 'O' quer dizer que na resposta deverá ser esperado por "OK". Mais uma vez - não está sendo tratado ainda nessa versão do programa.

A resposta ao comando AT é tratada pela função reader(), invocada através da flag em interrupts(), e verificada posteriormente na função main(). Nesse ponto, existe uma possibilidade de falha; o que acontece se a rede não responder? Bem, isso não está sendo tratado agora e utilizar um timer para temporizar a leitura seria interessante. Ainda há a possibilidade de haver resposta, mas ser diferente da esperada. Como tratar um erro de resposta do comando AT? A única coisa que me ocorre no momento é um reset():

void hardReset(){
    RESET_port = LOW;
    Delay_ms(50);
    RESET_port = HIGH;
}

Após o reset (convenhamos que fazê-lo no pino é menos trabalhoso), em dado momento o envio de comandos AT por parte do PIC voltará a ser efetivo, por isso considerei rodar essa versão do programa sem tratamento de exceções.

O próximo passo que tomei foi habilitar múltiplas conexões. Em uma chamada cíclica da função sendSample() todas as rotinas de comunicação se repetem, porém o único comando dessa rotina que é aplicado é o cipsend. Mas não importa, porque não causa erros de funcionamento e garante que em caso de reset de qualquer ponto, tudo volte a funcionar sem tratar logicamente qualquer condição.

void cipmux(){
    word = 'O';
    //libera a mensagem para a funcao write()
    write("AT+CIPMUX=1rn");
    Delay_ms(UART_DELAY);
}

A função de conexão TCP é a próxima da lista:

void tcpStart(){
    word = 'L';
    //garantir que o array de msg esta limpo, apesar de nao usado.
    /* A mensagem eh grande demais. Para compor uma string com o P16F883
    seria necessario manipular o banco de memoria atraves do bit IRP, mas
    nao seria uma tarefa trivial. Como o ESP8266 espera por rn (CF,LF ou
    para os puristas, 0x0D 0x0A), da pra ir escrevendo chuncks.
    */
    clear(text,MSGLEN);
    //reservar memoria pra fazer apenas 1 write nao daria porque o PIC16F883
    //reclama da alocacao de memoria. Assim resolve-se o problema e ainda
    //foram economizados muitos bytes.
    write("AT+CIPSTART=");
    Delay_ms(UDELAY);
    //strcat(text,TCPID);
    write(TCPID);
    Delay_ms(UDELAY);
    //strcat(text,',');
    write(",");
    Delay_ms(UDELAY);
    //strcat(text,""TCP",");
    write(""TCP",");
    Delay_ms(UDELAY);
    //strcat(text,""");
    write(""");
    Delay_ms(UDELAY);
    //strcat(text,SERVER);
    write(SERVER);
    Delay_ms(UDELAY);
    //strcat(text,"",");
    write("",");
    Delay_ms(UDELAY);
    //strcat(text,PORT);
    write(PORT);
    Delay_ms(UDELAY);
    //strcat(text,"rn");
    write("rn");
    Delay_ms(UART_DELAY);
}

  • "Porque essa linguiça de código?" - você pode dizer. Bem, eu digo. Esse PIC só tem 240 bytes de memória, eu já estava chegando no limite, então reduzi o tamanho do meu array de chars (que comportaria as variáveis grandes) para caber na MCU. Porém, acabei batendo de cara em outro problema; a memória é divida em bancos e não tinha mais espaço para comportar a variável do tamanho pretendido. Então testei enviar comandos para o ESP8266 e mandar o 'rn' só no final. E deu certo! Com isso, economizei MUITA memória e depois fui batendo o código. Em suma, de quase 240 bytes utilizados, consegui reduzir para 162 bytes. Essa é uma coisa importante a considerar. Certa vez um diretor de uma empresa que trabalhei me disse que "qualidade infinita tem custo infinito". E assim é. Atingi o objetivo com menos qualidade, mas também com menor custo, senão haveria de comprar um PIC mais parrudo. Do modo que está, creio que caiba em um PIC12F (8 pinos) que tenho aqui, me resta verificar se os pinos sobressalentes são suficientes para as flags (porque utilizei pinos da MCU como flags, de forma a poupar memória).

Apesar de não ser complexo, o envio de uma mensagem TCP é dividida em duas etapas; primeiro informa-se ao ESP8266 o ID da conexão a utilizar seguido do comprimento (em bytes) da mensagem a ser enviada - lembrando que CF/LF não devem ser contabilizados. Aguarda-se então a resposta e abrir-se-á um pseudo-prompt ">". Nesse momento a mensagem já pode ser enviada. MAS, e tão somente "MAS", eu mandei tudo sem validação, como já citei 4 outras vezes.

void sender(){
    sensorVal = getSensorValue();

     //clear usa count no loop. como a var eh reaproveitada, faz-se o clear
     //primeiro e depois calcula-se o tamanho de alocacao da msg
     clear(text,MSGLEN);
     
    //1 - aloca o tamanho da mensagem a enviar
    //10 = esp8266:X, ; -2 = rn ; final = esp8266:X,yrn onde X pode ter 2 bytes
    count = 10 + strlen(MYID); //rn nao entra na conta

    /* IntToStr precisa de no minimo 7 bytes, por isso eh melhor utilizar o array 
    text, depois copiar o resultado para cipsendSize, que soh ocupa 2 bytes.
    O lado negativo eh que um loop foi necessario.
    */
    IntToStr(count,text); //- - - - - - - - - - - - _ '1' '�'
    clear(cipsendSize,CIPSIZE);

    for (count=4;count<7;count++){ //0 1 2 3 [4] [5] [6] //se a posicao nao for em branco e nem terminador nulo... if (text[count] != ' ' && text[count] != 0){ if (cipsendSize[0] == 0){ cipsendSize[0] = text[count]; } else{ cipsendSize[1] = text[count]; break; } } } //limpa as variaveis utilizadas (count eh limpo pela funcao clear()) clear(text,MSGLEN); /*A mensagem a enviar eh dividida em 2 etapas: CIPSEND: Indica a conexao (pelo ID) e o tamanho da mensagem que sera enviada. Apos enviar essa mensagem, um prompt deve ser recebido na leitura. A interrupcao devera tratar a resposta, aguardando por um '>', entao a segunda fase eh o envio
    da mensagem, do tamanho informado nesse comando. O envio de '>' pelo ESP8266
    eh tratado na interrupcao, que informa ao reader() que tem dados. A funcao
    reader() le e se encontra '>', executa sensorStatus().
    */
    strcpy(text,"AT+CIPSEND="); //formata a primeira mensagem
    strcat(text,TCPID);  //escolha da conexao a utilizar (tcp 4)
    strcat(text,",");    //...
    strcat(text,cipsendSize); //... e concatena o tamanho da msg TCP
    strcat(text,"rn");
    
    clear(cipsendSize,CIPSIZE);
    word = '>';
    count = 0; //pena ter que fazer isso aqui, mas o contador precisa estar limpo
               //na interrupcao do reader
    write(text);
    Delay_ms(UART_DELAY);
}


Ficou convencionado o formato de mensagem "esp8266:id,status" (porque a definição é minha e eu escrevi tudo sozinho, então eu quero que seja assim) :-)

A função reader() é quem terá um pouco mais de inteligência, uma vez que deve tratar as respostas da interrupção, mas agora está um tanto quanto imprestável:

void reader(){
        READER_port = LOW;
        if (UART1_Data_Ready()){
           text[count] = UART1_Read(); //le o byte na posicao text[posicao]
           //mesmo que vier rn nao tem erro pq text tem espaco. O prompt abre na mesma linha, nao tem terminador
               if (text[count] == '>'){
                   sensorStatus();
                   DBG_port = !DBG_port; //TODO: remover o led de debug (?)
                   clear(text,MSGLEN);
               }
               else if (text[count] == 'n'){
                   text[count+1] = '�';
                   //se casar, limpa as variaveis
                   if (word == 'O'){
                       if (strcmp(text,"OKrn") == 0){ //encontrou!
                           //DEBUG no LED - TODO: remover
                           DBG_port = !DBG_port;
                           //se for r ou n, a linha acabou de qualquer modo
                           clear(text,MSGLEN);
                       }
                   }
                   //esse if eh uma repeticao estupida, mas por causa da memoria.
                   //do modo 'clean' estava ocupando 194 bytes, agora esta com
                   //173 bytes de RAM em uso.
                   else if (word == 'L'){
                       text[count+1] = '�';
                       if (strcmp(text,"Linkedrn") == 0){ //encontrou!
                           //DEBUG no LED
                           DBG_port = !DBG_port;
                           //se for r ou n, a linha acabou de qualquer modo
                           clear(text,MSGLEN);
                       }
                   }

               }
               count++;
        } 
}


A interrupção não mantém código, mas existe um conjunto de regras a obedecer (ver referencias anteriormente citadas):
1 - desligar a chave geral das interrupções, pois interrupção tem prioridade máxima e todo o restante do processamento é parado para recebê-la.
2 - limpar a flag da interrupção, senão ao voltar a chave geral, haverá uma interrupção imediatamente
3 - tratar a causa da interrupção (que é o esperado). Aí entra a função reader()
4 - levantar a chave geral para aguardar por novas interrupções:

void interrupt(){
    //desabilita a chave geral de interrupcoes pra nao chegar mais nada
    GIE_bit = 0;
    //se for UART...
    if (RCIF_bit == 1){ //ESSA INTERRUPCAO NAO ESTA EM USO
        // Checagem de erro de frame
        if(FERR_bit == YES) {
            rxRead = RCREG;
            GIE_bit = 1;
            return;
        }
        // overun
        if(OERR_bit == YES) {
            CREN_bit = 0;
            CREN_bit = 1;
            GIE_bit  = 1;
            return;
        }
        //RCIF_bit nao pode ser limpo por software, tem que ser assim nessa MCU;
        //lendo o RCREG para uma variavel
        count = RCREG;
        count = 0;
        
        //apontando essa flag, o reader() eh executado no loop principal
        READER_port = HIGH;
        
        //zera o timer e comeca novamente
        /*Se houve leitura UART, reset do timer ate que nao tenha mais mensagem
         a ler, entao temporiza os 10 segundos*/
        TMR0 = oneSecond = secs = 0;
    }
    //se for TMR0
    if (TMR0IF_bit){
        //zera o timer
        TMR0 = 0;
        TMR0IF_bit = 0; //essa eh a flag apos o overflow; limpando...
        oneSecond++; //temporizador de 1 segundo (acumula os estouros de TMR0)
        if(oneSecond > 999){   //prescaler 1:8 - 1000ms
            secs++; //somador de segundos (limpo no loop principal)
            //Apos ter acumulado um segundo de overflow, volta a 0
            oneSecond=0;
        }
    }
    //flags limpas e eventos tratados, basta levantar a chave geral novamente
    GIE_bit = 1;
}


O servidor

esp8266-qt-300x111.webp

A conexão TCP é feita como exemplificado nesse post. Mas aqui tem algumas implementações importantes, principalmente relacionado à manipulação das células dessa tabela que mantém o status dos dispositivos conectados.

Estou disponiblizando o programa em Qt sem finalizar também, porque nesse momento é só a prova de conceito para um projeto, mas adianto que as celulas receberão um label baseado em arquivo, utilizando o QSettings. Desse modo, poderia ser monitoramento de portas, por exemplo; ID 1 seria porta do banheiro, então o label seria BANHEIRO, mas se trocar o sensor de lugar pra monitorar a garagem, basta trocar o valor do label ID1 no arquivo labels.ini.

O código (bem simples) em Qt está(va) disponível em um link (que sumiu do Dropbox).

Obviamente esse código será melhorado, a aplicação em Qt receberá mais recursos etc. Essa foi apenas uma prova de conceito utilizando um PIC médio. Assim que eu fiz o video com alguns sensores, atualizo esse post.

Inscreva-se em noss canal DobitAoByte no Youtube!

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.