Para comunicar pelo padrão TCP/IP é preciso abrir um "socket'(canal de comunicação) , uma vez aberto o socket é possível comunicar via pagina http ou enviando os dados diretamente a um dispositivo, por este motivo neste exemplo demonstro como fazer as 2 interfaces.Para a comunicação via socket tudo que for enviado ao microcontrolador é devolvido(funcionando exatamente igual a um terminal serial rs-232).Para a comunicação http criei uma pequena página(pois este microcontrolador tem pouca memoria) que permite ligar ou desligar um led, essa pagina é acessada via navegador do google chrome(não funcionou muito bem no firefox e internet(pode ter sido alguma configuração do navegador)),para isso basta digitar o IP+Porta se for usada a porta 80(padrão) não será necessário digitar no buscador a porta.
Uma configuração de rede que deve ser respeitado é quanto ao DHCP(que pegar um ip automaticamente), no modulo deixei ele desligado para conseguir trabalhar com IP fixo, mas no PC pode até ficar habilitado embora eu recomendo que para a conexão entre pc e placa já deixe o DHCP desligado e coloque um IP diferente do modulo wifi, por exemplo aqui estou usando como gateway do modulo e IP o valor = 192.168.5.1 na porta 5000, então meu pc deve estar no ip 192.168.5.10 por exemplo e configure como gateway o IP 192.168.5.1 deixando a mascara com o valor 255.255.255.0.Se deixar o DHCP de ambos ligado toda vez que você se conectar será gerado um novo ip que talvez vc nem saiba qual será.
O modulo ESP8266 tem a possibilidade de trabalhar como "Acess point"(AP) ou "station"(estação de trabalho) e ainda como ambos, para este exemplo deixei ele como AP.Um ponto um pouco chato de se configurar este modulo é que as respostas dos comandos devem ser sempre checadas e não se tem a possibilidade de configurar ele todo de uma vez só, ou seja deve ser configurado parâmetro por parâmetro, para isso criei uma maquina de estados (Inicializa_ESP8266), que controla a inicialização do modulo.Dependendo de como for a configuração desejada alguns comandos retornam erros o que complica mais um pouco a situação nossa como programadores de firmware(como eu sofri com esse modulo pois não encontrei material sobre ele para pic somente para arduino).
Este modulo apesar de ser simples para configurar exige um certo conhecimento de comunicação TCP e arquitetura de redes sem fio, pois isso é fundamental para que se alcance o resultado desejado,pois parâmetros específicos como DHCP,canal de frequência da wifi, protocolo TCP/IP,etc... , com o exemplo aqui é possível ter um caminho de exemplo para que vocês possam prosseguir com seu projetos.Link pra baixar o programa para testar o modulo na ethernet(terminal): https://drive.google.com/file/d/0Bx9JeN0dmzXxaXh5MHI4aF9fT1U/view?usp=sharing ,com este programa você pode acessar dispositivos por meio de protocolo TCP/IP, roda no windows.
Vamos conhecer a aparência e pinagem do modulo ESP8266 na figura abaixo, juntamente com a pinagem do microcontrolador.
Fisicamente um limitante é o hardware que trabalha com tensões diferentes(3,3V e 5V) dai a solução que adotei foi a do esquemático a seguir,o ponto critico é que em altas velocidades da porta serial o microcontrolador deve trabalhar em 5V para não prejudicar a comunicação.A solução consiste de divisor resistivo para casar os valores de tensão.
Para alimentar o circuito usei os tradicionais LM7805, sendo um ligado ao microcontrolador e outro ligado a um segundo regulador LD1086V33(3,3V) e só então ligado ao modulo wifi.Coloquei 3 leds para indicações de status, conexão(abertura de socket) e falha(usado também para ser acionado pela web).Quando o circuito é alimentado o microcontrolador irá tentar configurar o modulo caso não dê certo pisca os leds verde e amarelo,caso contrário apenas o led verde pisca.
No firmware o único arquivo adicional é o da comunicação serial "serial_rs232.c", não uso o famoso "print" do código C por consumir mais memoria do que esta funções(pois as que implementei são mais enxuta, o que as vezes gera um pouquinho de trabalho mas o código se torna mais leve).
O código fonte:
/* * * Comunicando via WIFI com modulo ESP8566 * * Compilador : MPlabXC8 * Microcontrolador: 16F648A * Autor: aguivone * Versão: 1 * Data : 19 de janeiro de 2016 * Atualizações : 19/02/2016 - melhorando a inicialização * Atualizações : 23/02/2016 - melhorando a inicialização * Atualizações : 29/02/2016 - finalizado. */ #include <stdio.h> #include <stdlib.h> #include <string.h> //para usar funçoes de string deve se adicionar este header #define _XTAL_FREQ 20000000 // oscilador interno de 4 Mhz #include <xc.h> #include "serial_rs232.c" /////////////////////////////////////////////////////////configuraçôes////////////////////////////////////////////////// #define LED_CONEX RB3 #define LED_WEB RB5 #define LED_STATUS RB0 #define TAM_MAX 40 //tamanho maximo do buffer da serial #define NUM_MAX_TENTATIVA 3 //numero maximo de tentativa em cada comando #define TEMPO_RX 100 //tempo max entre caracteres recebidos(timeout) #define FIM_CONF 'x' #define TIMEOUT_CONF 40//40S//tempo maximo sem resposta de envio #pragma config FOSC = HS #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT ligado) para que o microcontrolador inicialize corretamente #pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD) #pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD enabled) #pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming) #pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off) #pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) //variaveis de tempo int giTempo_us=0; long glTempo_ms=0; long glTempo_led=0; long glTempo_rx=0; //variaveis da serial char gcCaracter=0; unsigned char gucStatus_serial='N';//N = sem dados,R= recebendo dados , F=fim de recepção unsigned char gucEstado_de_envio='0';//0 = sem dados,V=dados validados , N=dados não validado unsigned long gulTam_recebido=0; unsigned char gucBuffer_serial[TAM_MAX];//só pra testar //variaveis da maquina de estado unsigned char gucStado_do_modulo='A';//inicia em A unsigned long gulTempo_de_espera=0; unsigned long gulTimeout_de_envio=0; unsigned int guiTentativa_envio=0; //*******************************************************************************/ //=================================================================================== //Função: _limpa_buffer //Parâmetro: não tem. //Retorno: não tem. //Descrição: usada para limpar o buffer de recpção. //=================================================================================== void limpa_buffer() { long tamanho = 0; while(tamanho<TAM_MAX) { gucBuffer_serial[tamanho]=0X00; tamanho++; } } //*******************************************************************************/ //=================================================================================== //Função: _trata_int_conf //Parâmetro: não tem. //Retorno: não tem. //Descrição: usada para pegar as respostas das configurações. //=================================================================================== void trata_int_conf() { if(gucStatus_serial == 'R') {//primeiro dado gucBuffer_serial[gulTam_recebido] = gcCaracter; gulTam_recebido++; if(gulTam_recebido>(TAM_MAX-1)) { gucStatus_serial = 'N';//sobrescreve o vetor } } if(gucStatus_serial == 'N') {//primeiro dado gucBuffer_serial[0] = gcCaracter; gulTam_recebido =1; gucStatus_serial = 'R'; } } //*******************************************************************************/ //=================================================================================== //Função: trata_int_dados //Parâmetro: não tem. //Retorno: não tem. //Descrição: usada para pegar os dados(após a configuração). //=================================================================================== void trata_int_dados() { if(gcCaracter == '\n')//caracter de retorno de carro "LR" (depois vejo, talvez verificar se tem o \n)) {//final da frase if((gucBuffer_serial[gulTam_recebido-2]=='D')||(gucBuffer_serial[gulTam_recebido-2]=='T')) {//verifica se está abrindo ou fechando o socket(comunicação) if((gucBuffer_serial[gulTam_recebido-5]=='N')&&(gucBuffer_serial[gulTam_recebido-4]=='E')&&(gucBuffer_serial[gulTam_recebido-3]=='C')) {//abrindo conexão recebe "CONNECT" LED_CONEX = 1; gulTam_recebido=0;//zera variavel de tamanho gucStatus_serial = 'N';//prepara para pegar novos dados } if((gucBuffer_serial[gulTam_recebido-5]=='O')&&(gucBuffer_serial[gulTam_recebido-4]=='S')&&(gucBuffer_serial[gulTam_recebido-3]=='E')) {//fechando conexão recebe "CLOSED" LED_CONEX = 0; gulTam_recebido=0;//zera variavel de tamanho gucStatus_serial = 'N';//prepara para pegar novos dados } } } if(gucStatus_serial == 'R') {//está recebendo dados gucBuffer_serial[gulTam_recebido] = gcCaracter; gulTam_recebido++; if(gulTam_recebido>TAM_MAX-2) {//gera aviso de estouro pois o buffer de recpção não consegue armazenar o pacote todo. gucStatus_serial = 'E'; } } if(gucStatus_serial == 'N') {//primeiro caracter capturado gucBuffer_serial[0] = gcCaracter; gulTam_recebido =1; gucStatus_serial = 'R'; } } //*******************************************************************************/ //=================================================================================== //Função: _interrupcoes //Parâmetro: não tem. //Retorno: não tem. //Descrição: usada para tratar as interrupções geradas pelo microcontrolador. //=================================================================================== void interrupt interrupcoes(void)//vetor de interrupção { if(T0IF) {//rotinas de tempo(15us)) giTempo_us++; TMR0 = 254;//reinicia timer com o valor calculado T0IF = 0; if(giTempo_us>66)//1ms {//executa a cada 1ms giTempo_us = 0; glTempo_ms++; if(glTempo_rx > 0) { glTempo_rx--; } if(gulTempo_de_espera > 0) { gulTempo_de_espera--; } if(glTempo_led>0) { glTempo_led--; } if(glTempo_ms>1000) {//executa a cada 1s glTempo_ms=0; gulTimeout_de_envio++; } } //62.5us + os delay da rotinas = 75us } if(RCIF)//verifica interrupção da serial {//interrupção da serial gcCaracter = RCREG; if(gucStado_do_modulo == FIM_CONF+1) {//Modulo já foi configurado trata_int_dados(); } else { trata_int_conf(); } glTempo_rx = TEMPO_RX; RCIF =0;//limpa flag de interrupçao } } //=================================================================================== //Função: _inicializa_CPU //Parâmetro: não tem. //Retorno: não tem. //Descrição: configura o microcontrolador. //=================================================================================== void inicializa_CPU() { TRISA = 0XFF;//todas são entradas TRISB = 0X02;//configura portB como saida exceto rb2(pino RX)) PORTB = 0; // limpar as portas que estão configuradas como saidas CMCON = 0X07; ///////////inicializa o timer0 OPTION_REG = 0X03;//timer0 com prescaler dividido por 16 T0IE = 1;//habilita interrupção do timer0 TMR0 = 254;//zera registrador de contagem ///////////////////////////////////////////// inicializa_RS232_HS(115200,20);//modo de alta velocidade de clock // inicializa_RS232(9600,1,20);//na simulação(pra não sobrecarregar o simulador se necessario)) PEIE = 1;//habilita interrupção de perifericos do pic GIE = 1; //GIE: Global Interrupt Enable bit LED_CONEX=0; LED_WEB=0; } //=================================================================================== //Função: _Verifica_resposta //Parâmetro: char prox,char anterior,unsigned char procurar[]. //Retorno: não tem. //Descrição: verifica se o comando foi aceito. //=================================================================================== void Verifica_resposta(char prox,char anterior,unsigned char procurar[]) { if(glTempo_rx == 0)//espera receber o comando { if(BuscaPalavra(gucBuffer_serial,procurar) != 0XFFFF) { gucStado_do_modulo = prox;//vai para proximo passo guiTentativa_envio = 0; } else { if(guiTentativa_envio >= NUM_MAX_TENTATIVA) {///registra falha na inicialização gulTimeout_de_envio = 250;//Deu erro na configuração } else { gucStado_do_modulo = anterior;//passo anterior } } } } //*******************************************************************************/ //=================================================================================== //Função: Envia_comando //Parâmetro: unsigned char dados[],char mod,int multi_tempo. //Retorno: não tem. //Descrição: usada para enviar comandos. //=================================================================================== void Envia_comando(unsigned char dados[],char mod) { PEIE = 0;//desabilita interrupção de perifericos do pic gulTimeout_de_envio=0; gucStado_do_modulo = mod; limpa_buffer(); gucStatus_serial = 'N';//põe pra ir pro inicio do vetor gulTam_recebido =0;//zera o tamanho imprime_RS232(dados); glTempo_rx = TEMPO_RX*3;//seta tempo de recepção de dados um pouco maior para primeira amostra guiTentativa_envio++; PEIE = 1;//habilita interrupção de perifericos do pic } //=================================================================================== //Função: _Inicializa_ESP8266 //Parâmetros: não tem. //Retorno: não tem. //Descrição: é uma maquina de estado que configura o modulo ESP8266. //=================================================================================== //maquina de estado de inicialização do modulo void Inicializa_ESP8266(void) { switch(gucStado_do_modulo) { case 'A'://teste inicial pra saber se o modulo está ativo { Envia_comando("AT\r\n",'B');//comando , proximo passo }break; case 'B'://verifica resposta { if(glTempo_rx == 0)//espera receber o comando { if(BuscaPalavra(gucBuffer_serial,"OK") != 0XFFFF) { gucStado_do_modulo = 'C';//vai para proximo passo guiTentativa_envio = 0; } else { gucStado_do_modulo = 'A';//passo anterior } } }break; case 'C': { //configura o modo que o modulo vai funcionar, aqui deixei para funcionar como AP // Envia_comando("AT+CWMODE=3\r\n",'D');//comando , proximo passo - AP ou servidor Envia_comando("AT+CWMODE=2\r\n",'D');//comando , proximo passo - AP // Envia_comando("AT+CWMODE=1\r\n",'D');//comando , proximo passo - servidor }break; case 'D'://verifica resposta { Verifica_resposta('E','C',"OK");//colopra onde vai e qual é a palavra procurado }break; case 'E'://manda resetar o modulo { Envia_comando("AT+RST\r\n",'F');//comando , proximo passo }break; case 'F'://verifica resposta { gulTimeout_de_envio=0;//zera variavel de contagem while(gulTimeout_de_envio < 15);//espera pelo menos 10segundos para resetar com segurança gulTimeout_de_envio=0;//zera variavel novamente Verifica_resposta('G','E',"ready"); }break; case 'G': { //configura o modo que o modulo vai funcionar, aqui deixei para funcionar como AP Envia_comando("ATE0\r\n",'H');//pede pra não repetir o comando }break; case 'H'://verifica resposta { Verifica_resposta('I','G',"OK");//colopra onde vai e qual é a palavra procurado }break; case 'I': { Envia_comando("AT+CIPMUX=1\r\n",'J');//comando , proximo passo//cofigura para conexões multiplas // Envia_comando("AT+CIPMUX=0\r\n",'J');//comando , proximo passo//cofigura para conexao simples }break; case 'J'://verifica resposta { Verifica_resposta('L','I',"OK"); }break; case 'L'://configura DHCP e modo de operação { //quando DHCP é ligado ele busca ip automaticamente // Envia_comando("AT+CWDHCP=2,1\r\n",'M');//só aceitou o modo AP+STATION/dhcp ligado.(pega ip automatico)) // Envia_comando("AT+CWDHCP=2,0\r\n",'M');//AP+STATION/dhcp desligado.(para usar ip fixo)) Envia_comando("AT+CWDHCP=0,0\r\n",'M');//modo AP(o sinal fica mais estavel)/DHCP desligado(para usar ip fixo) }break; case 'M'://verifica resposta { Verifica_resposta('N','L',"OK"); }break; case 'N'://Nome do ponto de wifi,senha,canal(deixei o canal de freq.7)),tipo de segurança(0 = aberta)) { // Envia_comando("AT+CWSAP=\"MICROCONTROLARES-C\",\"senha\",7,0\r\n",'O'); }break; case 'O'://verifica resposta { Verifica_resposta('P','N',"OK"); }break; case 'P': { //configura para criar servidor na porta 5000 Envia_comando("AT+CIPSERVER=1,5000\r\n ",'Q'); }break; case 'Q'://verifica resposta { Verifica_resposta('R','P',"OK"); }break; case 'R': { //configura para criar servidor na porta 5000 Envia_comando("AT+CIPAP=\"192.168.5.1\" \r\n",'S');//usa este IP para gateway e para o servidor }break; case 'S'://verifica resposta { Verifica_resposta('T','R',"OK"); }break; case 'T': { //seta time out Envia_comando("AT+CIPSTO=100\r\n",'U');//comando , proximo passo }break; case 'U'://verifica resposta { gucStado_do_modulo = FIM_CONF;//não precisa de confimação }break; } } //=================================================================================== //Função: _envia_pacote //Parâmetros: long *tamanho,char *pacote[]. //Retorno: não tem. //Descrição: usado para o modo socket(sem usar uma pagina web, envia um pacote TCP). //=================================================================================== void envia_pacote(long *tamanho,char *pacote[]) { char valor[]="0000"; int pos = LongToChar(tamanho,valor); imprime_RS232("AT+CIPSEND=0,");//para conexão multipla informa o canal // imprime_RS232("AT+CIPSEND=");//na conexoes simples não precisa informar o canal while(pos<4) { escreve_RS232(valor[pos]); pos++; } escreve_RS232(0X0D); escreve_RS232(0X0A); glTempo_rx = TEMPO_RX/2; while(glTempo_rx > 0);//espera receber a resposta do comando pos=0;// while(pacote[pos] != 0X00 )//envia os dados recebidos novamente { //imprime na porta TCP os dados recebidos(o tamanho max aqui será uns 30 caracteres)) escreve_RS232(pacote[pos]); pos++; } escreve_RS232(0X0D); escreve_RS232(0X0A); } //=================================================================================== //Função: _envia_pagina //Parâmetros: não tem. //Retorno: não tem. //Descrição: Monta e envia a pagina HTML. //=================================================================================== void envia_pagina() {//lembre que \" = " logo conta como 1 caracter e não 2 limpa_buffer();//solicita pra limpar o buffer serial ///como vou conectar apenas 1 então o canal é 0 imprime_RS232("AT+CIPSEND=0,285");//para conexão multipla informa o canal (prepara envio e tamanho)) // imprime_RS232("AT+CIPSEND=");//na conexoes simples não precisa informar o canal escreve_RS232(0X0D); escreve_RS232(0X0A); glTempo_rx = TEMPO_RX; while(glTempo_rx > 0);//espera receber a resposta do comando imprime_RS232("<html><head><title>Teste</title></head><h1>PROGRAMA DE TESTE</h1>"); imprime_RS232("<body><p><form method=\"GET\"></p>"); imprime_RS232("<p><input type=\"radio\" name=\"Bot\" value=\"1\" CHECKED> Ligar led.</p>"); imprime_RS232("<p><input type=\"radio\" name=\"Bot\" value=\"0\" > Desligar led.</p>"); imprime_RS232("<p><input type=\"submit\" value=\"Enviar\"></p></body></html>"); escreve_RS232(0X0D); escreve_RS232(0X0A); } //////////////////////////////////////////////////////Rotina principal/////////////////////////////////////////////////////////////// //=================================================================================== //Função: _main //Parâmetros: não tem. //Retorno: não tem. //Descrição: função principal. //=================================================================================== ///////////////http://microcontroladores-c.blogspot.com.br////////////////////////////////////////// void main(void) { inicializa_CPU(); for(;;) { if(glTempo_led == 0) {//led de status LED_STATUS = ~LED_STATUS; if(gucStado_do_modulo == FIM_CONF+1) {//Modulo pronto pra funcionar glTempo_led = 100;//tempo mais rapido } else { glTempo_led = 500;//tempo normal if(gulTimeout_de_envio>TIMEOUT_CONF)//se for maior que 30S o tempo de espera então indica falha { LED_WEB = ~LED_WEB; //modulo não responde - falha } } } if(gucStado_do_modulo == FIM_CONF+1) {//Modulo pronto pra funcionar if((glTempo_rx == 0)&&(gulTam_recebido<8))//tamanho minimo em cada recpção de dados {//descarta gulTam_recebido=0;//zera variavel de tamanho gucStatus_serial = 'N';//prepara para pegar novos dados } if(gucStatus_serial == 'E')//espera terminar tempo de time out {//houve estouro do buffer logo a comunicação é via pagina HTTP if(BuscaPalavra(gucBuffer_serial,"GET") != 0XFFFF)//conexão por modo get {//pagina web solicitada ou resposta recebida if(BuscaPalavra(gucBuffer_serial,"GET / HTTP") != 0XFFFF)//solicitou pagina web envia_pagina(); if(BuscaPalavra(gucBuffer_serial,"GET /?Bot") != 0XFFFF)//resposta da pagina web { if(BuscaPalavra(gucBuffer_serial,"Bot=0") != 0XFFFF)//vê se é pra desligar led LED_WEB=0; if(BuscaPalavra(gucBuffer_serial,"Bot=1") != 0XFFFF)//vê se é pra desligar led LED_WEB=1; } } gulTam_recebido=0;//zera variavel de tamanho gucStatus_serial = 'N';//prepara para pegar novos dados } else {//é comunicação via socket if((glTempo_rx == 0)&&(gulTam_recebido>8))//tamanho minimo {//verifica se tem dados pra ler pois o tempo limite pra finalizar recpção já passou if(BuscaPalavra(gucBuffer_serial,"+IPD") != 0XFFFF) {//chegou dados validado long auxiliar = BuscaPalavra(gucBuffer_serial,":"); long tamanho ;//tamanho dos dados long pos_buffer=0;//tamanho dos dados unsigned char gucBuffer_tratamento[TAM_MAX]; if(auxiliar != 0XFFFF) {//pega o tamanho dos dados if(CharToInt(gucBuffer_serial[auxiliar-2])<10) {// o tamanho dos dados é maior que 10 tamanho = (10*CharToInt(gucBuffer_serial[auxiliar-2]))+CharToInt(gucBuffer_serial[auxiliar-1]); if(CharToInt(gucBuffer_serial[auxiliar-3])<10) { tamanho = (100*CharToInt(gucBuffer_serial[auxiliar-3]))+tamanho; } } else {//o tamanho é menor que 10 tamanho = CharToInt(gucBuffer_serial[auxiliar-1]); } auxiliar++;//pra não pegar o ":" while( pos_buffer < tamanho ) { //copia os dados para o vetor de tratamento gucBuffer_tratamento[pos_buffer] = gucBuffer_serial[auxiliar+pos_buffer]; pos_buffer++; } gucBuffer_tratamento[pos_buffer]=0X00;//caracter null } //limpa_buffer();//solicita pra limpar o buffer serial envia_pacote(tamanho,gucBuffer_tratamento); } gulTam_recebido=0;//zera variavel de tamanho gucStatus_serial = 'N';//prepara para pegar novos dados } } } else {//durante inicialização if(gucStado_do_modulo < FIM_CONF) {//inicializa o modulo if(gulTimeout_de_envio<TIMEOUT_CONF)//se for menor que 30S o tempo de espera terminar configuração { Inicializa_ESP8266(); } } if(gucStado_do_modulo == FIM_CONF) { ///neste momento a WIFI já esta configurada e pronta pra receber dados gucStado_do_modulo = FIM_CONF+1; } } }//loop infinito }
O código do arquivo serial:
/* * FUNÇOES USADAS NA SERIAL * * Compilador : MPlabXC8 * Microcontrolador: 16F877A * Autor: Aguivone * Versão: 1.0.1 * Descrição: ->Assim como o printf tem a função de comunicar via rs232, mas com o diferencial de ter um menor processamento e gasto de memoria * ->Mas exige um pouco mais de trabalho para imprimir caracteres numericos * Data de criação: 08/09/2014. * Data de atualização : 29/01/2016 - melhorando o baud rate para altas velocidades */ #include <xc.h> #include <string.h> //para usar funçoes de string deve se adicionar este header //=================================================================================== //Função: _inicializa_RS232 //Parâmetros: unsigned long ulVelocidade // : unsigned int uiModo //Retorno: não tem retorno. //Descrição: usada para iniciar a porta serial com velocidades até 38400. //=================================================================================== void inicializa_RS232(float fVelocidade,unsigned int uiModo,float fClock) {//// por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada. // verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado. RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono. float valor; if(uiModo == 1) {//modo = 1 ,modo alta velocidade TXSTA = 0X24;//modo assincrono,trasmissao 8 bits. // valor =(((_XTAL_FREQ/ulVelocidade)-16)/16);//calculo do valor do gerador de baud rate valor =(((fClock/fVelocidade)-16)/16);//calculo do valor do gerador de baud rate } else {//modo = 0 ,modo baixa velocidade TXSTA = 0X20;//modo assincrono,trasmissao 8 bits. //valor =(((_XTAL_FREQ/ulVelocidade)-64)/64);//calculo do valor do gerador de baud rate valor =(((fClock/fVelocidade)-64)/64);//calculo do valor do gerador de baud rate } SPBRG =(int)valor; // PIE1 = 0X20; RCIE = 1;//habilita interrupção de recepção TXIE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo) } //=================================================================================== //Função: _inicializa_RS232_HS //Parâmetros: unsigned long ulVelocidade // : unsigned int uiModo //Retorno: não tem retorno. //Descrição: usada para iniciar a porta serial com velocidades acima 38400. //=================================================================================== void inicializa_RS232_HS(float fVelocidade,int iClock)//o clock em mhz {//// por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada. // verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado. RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono. TXSTA = 0X24;//modo assincrono,trasmissao 8 bits.modo = 1 ,modo alta velocidade switch(iClock)//cadastrei somente os cristais mais usados { case 4: { if(fVelocidade > 57600) {//é 115200 SPBRG =1; } else {//é 57600 SPBRG =3; } }break; case 10: { if(fVelocidade > 57600) {//é 115200 SPBRG =4; } else {//é 57600 SPBRG =10; } }break; case 16: { if(fVelocidade > 57600) {//é 115200 ou 250000 if(fVelocidade == 115200) {//é 115200 SPBRG =8; } else {//é 250000 SPBRG =3; } } else {//é 57600 SPBRG =16; } }break; case 20: { if(fVelocidade > 57600) {//é 115200,250000,625000 ou 1250000(1,25Mbs)) if(fVelocidade == 115200) {//é 115200 SPBRG =10; } if(fVelocidade == 250000) { SPBRG =4; } if(fVelocidade == 625000) { SPBRG =1; } if(fVelocidade == 1250000) { SPBRG =0; } } else {//é 57600 SPBRG =21; } }break; } // PIE1 = 0X20; RCIE = 1;//habilita interrupção de recepção TXIE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo) } //=================================================================================== //Função: _escreve_RS232 //Parâmetros: char cValor //Retorno: não tem retorno. //Descrição: usada para escrever 1 caracter. //=================================================================================== void escreve_RS232(char cValor) { TXIF = 0;//limpa flag que sinaliza envio completo. TXREG = cValor; while(TXIF ==0);//espera enviar caracter } //=================================================================================== //Função: _imprime_RS232 //Parâmetros: const char ccFrase[] //Retorno: não tem retorno. //Descrição: usada para escrever uma string.(maximo 255 caracteres) //=================================================================================== void imprime_RS232(const char *ccFrase[]) { unsigned char indice = 0; unsigned char tamanho = strlen(ccFrase); while(indice < tamanho ) ///veja que o programa pode travar se aqui não tiver as duas aspas { escreve_RS232(ccFrase[indice]); indice++; } } //=================================================================================== //Função: _imprime_vetor_RS232 //Parâmetros: const char ccFrase[] //Retorno: não tem retorno. //Descrição: usada para escrever uma vetor de caracteres.(maximo 255 caracteres)) //=================================================================================== void imprime_vetor_RS232(unsigned char *ccFrase[]) { unsigned char indice = 0; unsigned char tamanho = strlen(ccFrase); while(indice < tamanho ) { escreve_RS232(ccFrase[indice]); indice++; } } //=================================================================================== //Função: _BuscaPalavra //Parâmetros: long ulQuant //Retorno: se = 0 não tem palavra igual, senão retorna a posição+1. //Descrição: buscar uma palavra em um vetor de caracteres(máximo 65535 caracteres) e retorna primeira posição) //=================================================================================== // As funçoes "strstr()" e "strspn()" não funcionaram bem por isto resolvi montar esta função //fica mais rapida int BuscaPalavra(unsigned char *ccFrase[],unsigned char *palavra[]) { unsigned int indice = 0; unsigned int tamanho_palavra = strlen(palavra); unsigned int tamanho_vetor = strlen(ccFrase) - tamanho_palavra;//tamanho do vetor subtraido do tamanho da palavra while(indice <= tamanho_vetor) //tamanho do vetor subtraido do tamanho da palavra { if(palavra[0]==ccFrase[indice]) {//se for parecido com a primeira letra da palavra então testa se é a palavra certa unsigned int tamanho_comparado=1;//inicia comparando próxima letra while(tamanho_comparado < tamanho_palavra) { if(palavra[tamanho_comparado]!=ccFrase[indice+tamanho_comparado]) { tamanho_comparado=tamanho_palavra+1; indice = indice + tamanho_comparado;//já pula pra frente pois foi testado) } tamanho_comparado++; } if(tamanho_comparado<tamanho_palavra+1) { return(indice); //retorna a posição da primeira ocorrencia da palavra } } indice++; } return(0XFFFF);//indica que não achou palavra igual } //=================================================================================== //Função: _LongToChar //Parâmetros: long ulQuant //Retorno: não tem retorno. //Descrição: usada para escrever um numero(int ou long) pela serial //=================================================================================== int LongToChar(long ulQuant,char *valor[]) { int ret=3;//indica apartir de quando pegar os dados para não imprimir 0 antes do numero if(ulQuant>=1000)//mas se quiser imprimir o zero basta usar o vetor inteiro {ret=0;} else {if(ulQuant>=100) {ret=1;} else {if(ulQuant>=10) {ret=2;} } } while(ulQuant>=1000) { ulQuant=ulQuant-1000; valor[0]++; } while(ulQuant>=100) { ulQuant=ulQuant-100; valor[1]++; } while(ulQuant>=10) { ulQuant=ulQuant-10; valor[2]++; } while(ulQuant>=1) { ulQuant=ulQuant-1; valor[3]++; } return(ret); } //=================================================================================== //Função: _CharToInt //Parâmetros: unsigned char convert_char //Retorno: retorna um numero. //Descrição: usada para conveter um caracter em um numero //=================================================================================== int CharToInt(unsigned char convert_char) { switch(convert_char) { case '0': return(0); case '1': return(1); case '2': return(2); case '3': return(3); case '4': return(4); case '5': return(5); case '6': return(6); case '7': return(7); case '8': return(8); case '9': return(9); default: return(10); } }
Depois de tanto trabalho o resultado pode ser visto no vídeo abaixo, a página http pode ser acessado por um smartphone também(testei com um samsung com android).
O vídeo de demonstração:
Até a próxima!
Aguivone, você simplesmente é fantástico.
ResponderExcluirJá aprendi varias coisas com você, e estava procurando informações sobre este modulo para usar com PIC, só encontrei para Arduino.
Parabéns, e continue sempre compartilhando.
Abraço !
Obrigado José Ricardo,a unica coisa que recebo por compartilhar é a satisfação de quem acompanha o blog,como não faço isso por dinheiro,mas porque compreendi que compartilhar provoca a evolução de todos que tem acesso ao conteúdo. Bom trabalho(ou estudos)!
ResponderExcluirMuito bom!!!
ResponderExcluirMuito bom, realmente tem pouco material sobre isso... Obrigado!!
ResponderExcluirMuito bom, realmente tem pouco material sobre isso... Obrigado!!
ResponderExcluirOlá, amigo!
ResponderExcluirGostei bastante desse teu tutorial. bem direto e prático. Só fiquei com uma dúvida a respeito da comunicação serial: Como faço para configurar a interface serial do lado do módulo wifi? É por comandos AT também? Ele já vem com uma configuração padrão para poder sair comunicando via serial?
Obrigado!
sim vem com comandos AT, porem vc pode comunicar direto pela interface do arduino, veja alguns artigos que postei sobre isso(faça uma busca no site).
ExcluirParabéns pelo gesto nobre em compartilhar as informações. Somente pessoas de bom caracter se propõe a dividir aquilo que lhe custou tanto trabalho para alcançar.
ResponderExcluirMuito bom o seu projeto, parabéns!!
ResponderExcluirEu estou tendo uma dúvida para gravar o programa no ESP8266, você usou o próprio gravador do pic ou usou um módulo UART? Eu posso usar o arduino para conectar o ESP ao usb e jogar o programa para ele?
Olá Aguivone,
ResponderExcluirObservando seu post no link : http://microcontroladores-c.blogspot.com.br/2016/02/pic-16f648a-comunicando-via-wifi.html?m=1
Muito bacana tudo o que você descreve e a forma simples que você passa com todas as informações...
Você poderia passar também o código fonte do html e/ou do aplicativo terminal_ethernetV1_3 ?
Muito grato por tudo ,
sergio
codigo do html que aparece no video....
ResponderExcluirVocê é um gênio! Parabéns!!!
ResponderExcluirO programa do PC que disponibilizou no site "terminal_ethernet.exe" e apagado pelo Norton antivírus logo quando executo. O que será que acontece? o Norton não aprova.
ResponderExcluirE porque por padrão os antivirus exclui arquivos ".exe", como é um programa gerado no visual studio e não tem certificado digital, então ele cria esses alertas,o único requisito que tem é ter instalado o framework 3.5 ou superior na maquina(o que é feito gratuitamente no site da microsoft).Penso que deve ser esse o motivo, valeu pelo feedback.
ExcluirPara desenvolver com arquitectura AVR, quais são as principais diferenças no código?
ResponderExcluiracesse essa outra matéria e veja as vantagens de grava-lo sem um microcontrolador http://microcontroladores-c.blogspot.com.br/search?q=wifi
ExcluirOlá Aguivone, pode me ajudar em uma questão ? Se trabalho com oscilador interno de 4mhz ou 8mhz, como faço para configurar o esp8266 para trabalhar a RS232 com esta limitação de velocidade ?
ResponderExcluirEm se tratando de comunicações é boa pratica usar o oscilador externo a cristal por ser mais confiável do que o oscilador RC interno.Mas para usar o oscilador interno use:
Excluir#pragma config FOSC = INTOSCIO
vc também poder ir na aba :WINDOWS->PIC MEMORY VIEWS -> CONFIGURATION BITS e setar os parâmetros que vc quer depois é só clicar no botão GENERATION SOUCE CODE TO OUTPUT , que aparecerá o código pronto pra vc copiar para seu código. espero ter ajudado!
Olá, obrigado pelo retorno, mas ... o modulo esp8266 já vem pré configurado para velocidade de 115200 baud e para esta velocidade necessito de um oscilador de 20mhz. Independente de ser interno ou nao, necessito trabalhar com velocidade do oscilador de 4mhz. Como confirar o esp8266 para aceitar uma frequencia de baud menor ?
Excluirgrato e no aguardo,
sergio
para alterar a frequencia de trabalho do ESP8266:
Excluir==>primeiramente faça um projeto com cristal externo de 20mhz ( oscilador interno de 20mhz tambem funciona, só que irá precisar de um PIC que suporte esta velocidade para oscilador interno)
==>neste projeto com cristal externo faça apenas a configuracao do modulo esp8266, para o baun que deseja, neste caso vou altera-lo para 19200
nao coloque nenhum codigo neste projeto, apenas faça a configuracao de comunicação serial do PIC com o ESP8266(usando CCS compiler):
#use delay(clock=20000000)
#use rs232(baud=115200, xmit=pin_c4, rcv=pin_c5)
#FUSES HS
void main(void){
puts("AT+UART_DEF=19200,8,1,0,0");//aqui muda o modulo para o novo baun e pronto
while(true){}
}
==>pronto, pegue o modulo esp8266 que já está configurado para 19200 e utilize-o em seu novo projeto, com oscilador de 8mhz que irá funcionar.
Este comentário foi removido pelo autor.
ResponderExcluirOlá Aguivone. Muito bom seus posts, show de bola. Bom estou tendo um erro aqui ao compilar o código e não estou sabendo como arrumar, poderia me dar uma força? Seguinte o erro que esta ocorrendo são esse:
ResponderExcluirserial_rs232.c:41: warning: (373) implicit signed to unsigned conversion
serial_rs232.c:152: warning: (359) illegal conversion between pointer types
pointer to pointer to const unsigned char -> pointer to const unsigned char
serial_rs232.c:155: warning: (358) illegal conversion of pointer to integer
serial_rs232.c:168: warning: (359) illegal conversion between pointer types
pointer to pointer to unsigned char -> pointer to const unsigned char
serial_rs232.c:171: warning: (358) illegal conversion of pointer to integer
serial_rs232.c:186: warning: (359) illegal conversion between pointer types
pointer to pointer to unsigned char -> pointer to const unsigned char
serial_rs232.c:187: warning: (359) illegal conversion between pointer types
pointer to pointer to unsigned char -> pointer to const unsigned char
main.c:71: warning: (373) implicit signed to unsigned conversion
main.c:233: warning: (359) illegal conversion between pointer types
pointer to unsigned char -> pointer to pointer to unsigned char
main.c:233: warning: (359) illegal conversion between pointer types
pointer to unsigned char -> pointer to pointer to unsigned char
main.c:233: warning: (373) implicit signed to unsigned conversion
main.c:266: warning: (359) illegal conversion between pointer types
as vezes o compilador dá esses warning(que não é erro) mas o código funciona,em alguma linhas basta vc declarar a variável como o compilador está pedindo ai.
Excluirpointer to unsigned char -> pointer to pointer to const unsigned char
ResponderExcluirmain.c:290: warning: (359) illegal conversion between pointer types
pointer to unsigned char -> pointer to pointer to unsigned char
main.c:290: warning: (359) illegal conversion between pointer types
main.c:500: warning: (359) illegal conversion between pointer types
pointer to unsigned char -> pointer to pointer to unsigned char
main.c:500: warning: (359) illegal conversion between pointer types
pointer to const unsigned char -> pointer to pointer to unsigned char
main.c:506: warning: (373) implicit signed to unsigned conversion
main.c:508: warning: (373) implicit signed to unsigned conversion
main.c:508: warning: (373) implicit signed to unsigned conversion
main.c:509: warning: (373) implicit signed to unsigned conversion
main.c:511: warning: (373) implicit signed to unsigned conversion
main.c:516: warning: (373) implicit signed to unsigned conversion
main.c:521: warning: (373) implicit signed to unsigned conversion
main.c:521: warning: (373) implicit signed to unsigned conversion
main.c:524: warning: (373) implicit signed to unsigned conversion
main.c:527: warning: (357) illegal conversion of integer to pointer
main.c:527: warning: (359) illegal conversion between pointer types
pointer to unsigned char -> pointer to pointer to unsigned char
"C:\Program Files (x86)\Microchip\xc8\v1.42\bin\xc8.exe" --chip=16F628A -G -mdist/default/production/WifiProject.X.production.map --double=24 --float=24 --opt=+asm,+asmfile,-speed,+space,-debug,-local --addrqual=ignore --mode=free -P -N255 --warn=-3 --asmlist -DXPRJ_default=default --summary=default,-psect,-class,+mem,-hex,-file --output=default,-inhx032 --runtime=default,+clear,+init,-keep,-no_startup,-osccal,-resetbits,-download,-stackcall,+clib --output=-mcof,+elf:multilocs --stack=compiled:auto:auto "--errformat=%f:%l: error: (%n) %s" "--warnformat=%f:%l: warning: (%n) %s" "--msgformat=%f:%l: advisory: (%n) %s" --memorysummary dist/default/production/memoryfile.xml -odist/default/production/WifiProject.X.production.elf build/default/production/main.p1 build/default/production/serial_rs232.p1
Microchip MPLAB XC8 C Compiler (Free Mode) V1.42
Build date: Apr 12 2017
Part Support Version: 1.42
Copyright (C) 2017 Microchip Technology Inc.
License type: Node Configuration
:: warning: (1273) Omniscient Code Generation not available in Free mode
serial_rs232.c:24: error: (237) function "_inicializa_RS232" redefined
serial_rs232.c:53: error: (237) function "_inicializa_RS232_HS" redefined
serial_rs232.c:137: error: (237) function "_escreve_RS232" redefined
serial_rs232.c:149: error: (237) function "_imprime_RS232" redefined
serial_rs232.c:165: error: (237) function "_imprime_vetor_RS232" redefined
serial_rs232.c:183: error: (237) function "_BuscaPalavra" redefined
serial_rs232.c:218: error: (237) function "_LongToChar" redefined
serial_rs232.c:259: error: (237) function "_CharToInt" redefined
(908) exit status = 1
make[2]: *** [dist/default/production/WifiProject.X.production.hex] Error 1
nbproject/Makefile-default.mk:147: recipe for target 'dist/default/production/WifiProject.X.production.hex' failed
make[2]: Leaving directory 'C:/Users/wmosc/Documents/MPLABXProjects/WifiProject/WifiProject.X'
make[1]: *** [.build-conf] Error 2
nbproject/Makefile-default.mk:90: recipe for target '.build-conf' failed
make[1]: Leaving directory 'C:/Users/wmosc/Documents/MPLABXProjects/WifiProject/WifiProject.X'
nbproject/Makefile-impl.mk:39: recipe for target '.build-impl' failed
make: *** [.build-impl] Error 2
BUILD FAILED (exit value 2, total time: 7s)
poderia me dar uma luz de como resolver?
Valeu
as vezes o compilador dá esses warning(que não é erro) mas o código funciona,em alguma linhas basta vc declarar a variável como o compilador está pedindo ai.Mais embaixo é por que vc adicionou 2 vezes o mesmo arquivo da lib, por isso as vezes adiciono direto no projeto sem chamar o "arquivo.C" ou então chamo o "arquivo.c" e não adiciono no projeto pela aba.
ExcluirBom Dia a todos(as);
ResponderExcluirPoderiam por favor, me ajudar a escrever um programa, em que eu consiga usar o ESP 8266_12e para controlar 4 relés e uma câmera ip? No caso eu quero ver a imagem da câmera e mandar atuar um relé. É tipo para apagar uma luz?
Grato pela atenção!
gleisonal@gmail.com
Aguivone, boa tarde!
ResponderExcluirMuito bom o seu projeto! Parabéns!
Eu estou tentando colocar pra funcionar mas até o momento não consegui..rsrs Eu tenho todos os dispositivos que você utilizou, compilei o código no MPLAB com o XC8 tranquilamente, passei o código para o PIC, porém os LEDs não acendem. Estou utilizando os mesmos reguladores de tensão, cristal, capacitor, resistores..etc Usei até um capacitor de desacoplamento, porém sem sucesso. Com isso, gostaria de saber se tem alguma ideia do que posso estar fazendo errado?
usou para o PIC16F877A? Estou querendo saber se funciona com o MPLAB, e XC8, se funcionar avise aqui por favor, obrigada!
ExcluirLaura, bom dia!
ExcluirNão, utilizei apenas para o PIC16F648A.
Aguivone, bom dia!
Acredito que tenha algo errado no código, pois eu habilitei uma saida para acender um LED após a função inicializa_CPU() chamada no main(), e o mesmo não acendeu. Com isso fui seguindo as funções e habilitando a saida e percebi que o código para após iniciar a função interrupt. Da função pra baixo ele não faz mais nada.
boa tarde amigão posso usar o PIC16F648-A normalmente no lugar do 628-A? vai funcionar normalmente??
ResponderExcluirParabéns pelo trabalho.
ResponderExcluirBoa tarde. Parabéns.
ResponderExcluirEstou fazendo um projeto de comunicação entre dois pics 628a. E esp8266. Afinando botões em um pic e acionando saídas do outro pic.
Nunca fiz isso mais o seu projeto já me deu pelo menos um caminho.
Muito obrigado.
esp-01-TX <----> pic-TX / esp-01-RX <----> pic-RX / Devia ser trançados
ResponderExcluir