sexta-feira, 27 de agosto de 2021

Acionando os pinos de I/O do C8051F382 em C

Olá pessoal!        
        Marcando meu retorno aos exemplos de chips e tecnologias utilizadas na área de pesquisa e desenvolvimento vou colocar alguns exemplos de como utilizar os microcontroladores da Silicon Labs, em especifico os da serie C8051F38XX que apresentam um alto desempenho porém tem como empecilho a falta de uma memória rom interna como os PIC´s e AVR´s, outro ponto negativo é o gravador desses microcontroladores que são mais caros e mais difíceis de encontrar.
        O software de programação utilizado será o Simplicity Studio e o toochain  "Keil C51 9.51", quanto a este software gostei muito pois apresenta poucos bugs e as funções em C são bem tranquilas de se utilizar.
como programa inicial vou postar um exemplo de como acionar os pinos deste microcontrolador, uma observação é o PORT4 que apresenta um comportamento diferenciado em relação ao outros pinos.
        Um complicador em relação a outros modelos de microcontroladores é o "Digital
Crossbar" que deve ser setado corretamente pois conforme se configura os pinos com funções especiais ele acaba por mudar também a função dos outros pinos com funções especiais, ou seja se vc configura um pino como interrupção ele poderá te impedir de usar um outro pino que tem uma outra função, te obrigando a escolher outro pino, porem uma vantagem é poder "chavear funções de hardware para mais pinos. A figura a seguir mostra como é essa arquitetura(retirado do datasheet do fabricante).Esse recurso pode ser desabilitado por meio dos registradores PXSKIP (onde em X é colocado o PORT em questão).
        
       Segue abaixo um exemplo de código que usa o PORT0 e o PORT4, para ler 2 botões e acionar leds através destes pinos.

O Código fonte :

// Target:         C8051F382
// Tool chain:     Simplicity Studio / Keil C51 9.51
// Autor: Aguivone Moretti Fogia
// Data: 27/08/2021
//
//exemplo de como usar botoes e leds
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <SI_C8051F380_Register_Enums.h>

//-----------------------------------------------------------------------------
// Global Constants
//-----------------------------------------------------------------------------

#define SYSCLK       12000000          // Internal oscillator frequency in Hz
//entradas porta 0
SI_SBIT(BOT1, SFR_P0, 6);                  // BOT1 ='0' botão pressionado
SI_SBIT(BOT2, SFR_P0, 7);                  // BOT2 ='0' botao pressionado

unsigned char porta4 = 0XFF;

//Saidas porta 0
SI_SBIT(LED1, SFR_P0, 0);                      // LED='0' Ligado   //pois ligado ao VCC
SI_SBIT(LED2, SFR_P0, 1);                      // LED='1' Desligado
// A porta 4 tem um funcionamento diferente em relação as outras
//ledes ligado ao port 4
#define LED3  0x7F
#define LED4  0xBF


//-----------------------------------------------------------------------------
// Function Prototypes
//-----------------------------------------------------------------------------
SI_INTERRUPT_PROTO(PCA0_ISR, PCA0_IRQn);

void OSCILLATOR_Init (void);
void PORT_Init (void);
void Liga_Port4(const char pino);
void Desliga_Port4(const char pino);
bool flag_teste;
//-----------------------------------------------------------------------------
// SiLabs_Startup() Routine
// ----------------------------------------------------------------------------
// This function is called immediately after reset, before the initialization
// code is run in SILABS_STARTUP.A51 (which runs before main() ). This is a
// useful place to disable the watchdog timer, which is enable by default
// and may trigger before main() in some instances.
//-----------------------------------------------------------------------------
void SiLabs_Startup (void)
{
   PCA0MD = 0x00;                      // Disable watchdog timer
}

//-----------------------------------------------------------------------------
// main() Routine
//-----------------------------------------------------------------------------

void main (void)
{
   uint16_t LOOP;
   uint16_t LOOP_max =65000;
   PORT_Init ();                       // Initialize crossbar and GPIO
   OSCILLATOR_Init ();                 // Initialize oscillator
   LOOP =0;
   IE_EA = 0;
   flag_teste = 0;
   while (1)
   {
     LOOP++;
         if(LOOP>LOOP_max)
         {
              Liga_Port4(LED3);
              LED1=0; //liga led
              LOOP=0;
         }
         if(LOOP ==(LOOP_max/2))
             {
              Desliga_Port4(LED3);
              LED1=1; //desliga led
             }
         if(BOT1)
           {
             Liga_Port4(LED4);
           }
           else
             {
               Desliga_Port4(LED4);
             }
        if(BOT2)
           {
              LED2=0; //liga led
           }
           else
             {
               LED2=1;//desliga led
             }
   }
}

//-----------------------------------------------------------------------------
// tratamento especial para port 4
//-----------------------------------------------------------------------------
void Desliga_Port4(const char pino)
{//desliga em nivel alto
   porta4 = porta4 | (~pino);
   P4 = porta4;
}
void Liga_Port4(const char pino)
{
  porta4 = porta4 & pino;
  P4 = porta4;
}
//-----------------------------------------------------------------------------
// Initialization Subroutines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// OSCILLATOR_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This function initializes the system clock to use the internal oscillator
// at 12 MHz.
//
//-----------------------------------------------------------------------------
void OSCILLATOR_Init (void)
{
   OSCICN = 0x83;                      // Set internal oscillator to run  (desabilitou osc interno)
                                       // at its maximum frequency(pois setou os 2 ultimos bit)
   CLKSEL = 0x00;                      // Select the internal osc. as (SYSCLK/4)
                                       // the SYSCLK source
}

//-----------------------------------------------------------------------------
// PORT_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This function configures the crossbar and GPIO ports.
//
// P0.0   digital   open-drain     PCA0 CEX0
// P0.1   digital   push-pull
//
//-----------------------------------------------------------------------------
void PORT_Init (void)
{
      OSCICN  = 0X81;                     // IHFOsc Enable, OpcDiv:3MHz
      CLKMUL  = 0XC0;                     // talvez nem precise neste microcontrolador -> habilita  e inicializa pll
      CLKSEL  = 0X03;                     // SYSCLK derived from the Internal High-Frequency Oscillator.
      PFE0CN  = 0X20;                     //(para High Speed) Prefetch Engine Control,otimiza tempo de execução lendo 2 bytes de execução por vez
      /* FLSCL.FOSE(7) = 0 for < 10MHz and 1 for SYSCLK > 10MHz
      FLSCL.FLRT(4) = 0 for < 25MHz and 1 for SYSCLK > 25MHz */
      FLSCL   = 0x90;
      CKCON   = 0x30;
      SPI0CN  = 0x02;                     //Select Mode 3-Wire pra liberar pino NSS

      P0MDIN  = 0xFF;                     //  are digital
      P1MDIN  = 0xFF;                     //  are digital
      P2MDIN  = 0xFF;                     //  are digital
      P3MDIN  = 0xFF;                     //  are digital
      P4MDIN  = 0xFF;                     //  are digital

      P0MDOUT = 0x10;                     // enable Switches as open-drain
      P1MDOUT = 0x00;                     // enable Switches as open-drain
      P2MDOUT = 0x00;                     // enable Switches as open-drain
      P3MDOUT = 0x00;                     // enable Switches as open-drain
      P4MDOUT = 0x04;                     // enable Switches as open-drain

      P0SKIP = 0xCF;                      // TXeRX precisa do Skip pra funcionar
      P1SKIP = 0xFF;
      P2SKIP = 0xFF;
      P3SKIP = 0xCF;

      XBR0   = 0x01;                     // CP1AE,CP1E,CP0AE,CP0E,SYSCKE,SMB0E,SPI0E,(UART0E)
      XBR2   = 0x00;                     // RESRV,RSRV,RESRV,RESRV,RESRV,RESRV,SMB1E, UART1E
      XBR1   = 0xC2;                     // WEAKPUD,(XBARE),T1E,T0E,ECIE,(PCA0ME):000:Nenhum,001:CEX0,010:CEX0eCEX1,...
      // VDM0CN -> monitor de tensão de reset
      VDM0CN = 0x80;                    //Colocado porque senão da problema na gravacao da flash(já estava isso)

      P4 = 0xD8        ;  //portas de p4 desligadas
     // P4 = 0        ;  //portas de p4 ligadas
}

Formatado por : http://hilite.me/  27/08/2021
Formatado por : http://hilite.me/ 27/08/2021

domingo, 8 de dezembro de 2019

Medidor do fator de potência com o PIC 18F4550 - Wattímetro e frequêncímetro.

Olá pessoal!

    Neste exemplo os conceitos abordados serão:
  • Uso do conversor analógico digital
  • Implementação da comunicação em 4 bits utilizando LCD(funciona para 16X2 ou 4X20).
  • Leitura de tensão AC e DC .
  • Medir o fator de potência.
     A intenção é criar um voltímetro,amperímetro,wattímetro,frequencímetro e medidor do fato de potência que por consequência deve ler tensão e corrente DC E AC.O programa se torna extenso mas se for entendido por partes ficará mais fácil o que cada bloco de código faz.
      Para o amperímetro é usado um resistor de 1 R ligado a uma carga(neste caso foi usado uma associação indutor e resistor para ter defasagem das senoides, para o voltímetro foi usado um divisor de tensão que mede até 30V porém não fica tão preciso devido ao numero de bits do conversor AD interno(10 bits).Para o Wattímetro é usado apenas os dados de tensão e corrente para fornecer a potencia instantânea, já para o caso de tensão DC a potencia media é feita ao longo do tempo(pois pode haver variações de tensão e corrente).O circuito montado para testes e simulação é mostrado na figura a seguir:
    Figura 1 - Esquemático geral(elaboração própria)

        Para medir o fator de potencia poderia ter sido feito internamente, com o microcontrolador, porém montar um simples circuito detector de zero com uma porta XOR é uma solução interessante, pois então só é preciso pegar a forma de onda que é proporcional a defasagem do sinal na saida da porta XOR. Porém durante os testes foi observado que esta porta XOR gera dois pulsos, e que o segundo é do momento que as ondas descem, o que gera imprecisão devido ao tempo de decaimento da tensão na saída da porta, na figura 2,em amarelo sinal de saída de XOR, em azul a tensão em vermelho a corrente do circuito.Com este circuito interno foi utilizado apenas uma porta do microcontrolador para medir a largura do sinal(período) e do pulso(fator de potência), por meio das interrupções do ccp1.

                                   Figura 2 - Sinais lidos no circuito de detecção de zero(elaboração própria)
   
      Outra observação é que para simular no proteus, ora se usa a fonte DC, ora se usa a fonte AC em simulações separadas, para alternar entre o modo AC e DC basta apertar o botão de seleção,na figura a seguir é mostrado as simulações  AC e DC.No modo AC a tensão e corrente medida são as de pico, para converter para RMS(que é a mostrada nos multímetros), basta dividir por raiz de 2, conforme figura AC ,figura 4.
          Figura 3 - Modo DC(elaboração própria)

Figura 4 - Modo AC (elaboração própria)

O circuito funcionando é visto na figura 5, veja que ele tem 2 botões sendo um para reset do microcontrolador e outro para alternar os modo de funcionamento. 
                                         Figura 5 - foto do protótipo implementado(elaboração própria)

O código fonte:

/* 
 * Author: Aguivone Moretti Fógia
 * descrição: Multimetro que mede fator de potencia
 * freq.de clock: 20MHZ
 * data:08/12/2019 
 * testado fisicamente.
 */
#include "MCC18_18F4550.h"
#include "tempo_20mhz.h"
#include "LCD_4bits.h"
unsigned char Buffer_lcd[16]; // variavel usada pelo LCD
//variaveia globais para cpp1////////////////////////////////
unsigned int tempo_pulso=0;
unsigned int periodo=0;
unsigned char estado_amostras=0;
unsigned int tempo_ccp1=0;
//variaveis globais para ADC
unsigned char modo_tensao=0;//padrão CC
unsigned char descarta_FP=0;
float PM[5];//pega os ultimos 5 valores para potencia media DC
//defines usados
#define sin1_FP PORTCbits.RC4
#define sin2_FP PORTCbits.RC5
#define BOT_SEL PORTEbits.RE2
#define LED_STATUS PORTEbits.RE1
#define LED_MODO   PORTEbits.RE0 //sinaliza modo AC e pisca a cada pulso de clock
//equações usadas
#define EQ_TENSAO (float)((lido_adc*35.2)/1023) //para divisor de 10K - 1K - Máx: 33V
#define EQ_CORRENTE (float)((lido_adc*5.0)/1023)
#define EQ_FP (float)((90.0*tempo_pulso)/periodo) //fator de potencia
#define EQ_FREQ  (float)((1/(0.0000002*periodo*2.0))/8)//para 20Mhz - divide por 8 pois o T1CON está assim//frequencia
unsigned char sinal_FP= ' ';
////prototipos de funções ///////////////////////
void Inic_Regs (void);
void Read_ADC_DC(void);
void Read_ADC_CA(void);
void high_isr (void);
void Tela_inicial(unsigned char modo);
void verifica_botao(void);
//void low_isr (void);
//vetor de interrupção de alta prioridade
#pragma code high_vector=0x08
void interrupt_at_high_vector(void)
{
    _asm GOTO high_isr _endasm        //desvia programa para rotina de tratamento da interrupção
}
#pragma code 
//*****************************
//Rotina de tratamento de interrupção (ISR)
#pragma interrupt high_isr 
void high_isr (void)
{
   if(PIR1bits.CCP1IF)
    {
        if(estado_amostras==0)
        {//prepara contagem
           TMR1L =0;//Zera timer 1
           TMR1H =0;//Zera timer 1           
           if((sin2_FP)&&(sin1_FP))//se os dois estiver em um indica que está na descida do sinal e isso gera imprecisão
           {//descarta
               estado_amostras=0;               
           }
           else
           {//sinal subindo               
               if(sin1_FP)
                   {sinal_FP= '-';}
               else{sinal_FP= '+';}
               CCP1CON = 0X04;//captura a cada borda de descida
               estado_amostras=1;
           }            
           
        }
        else
        {
            if(estado_amostras==1)
            {//borda de descida - pega valor do pulso
                tempo_pulso =((((unsigned int)CCPR1H)<<8)|CCPR1L);
                estado_amostras = 2;
                CCP1CON = 0X05;//captura a cada borda de subida 
                _delay_ms(1);//
                if((sin2_FP==0)||(sin1_FP==0))//se os dois estiver em zero indica que está na descida do sinal e isso gera imprecisão
                           {//sinal invalido  
                             estado_amostras = 0;
                           }
            }
            else
            {//fim de coleta
                if(estado_amostras == 2)
                {   
                 periodo =((((unsigned int)CCPR1H)<<8)|CCPR1L);
                 estado_amostras=3;//pra sinalizar fim de amostragem  
                 PIE1bits.CCP1IE = 0;//não precisa mais das interrupçõe do CCP1
                 PORTDbits.RD3 = 0;
                }
            }
        }        
        PIR1bits.CCP1IF = 0;//limpa flag   
    }
    
}
/////rotina principal/////////////////////////////
void main() 
{
  Inic_Regs();
  inicializa_LCD();
  Tela_inicial('D');
  modo_tensao=0;
  while(1)
   { 
     verifica_botao();
     if(modo_tensao)//modo AC
     {
        Read_ADC_CA();
        tempo_ccp1=0; 
        estado_amostras=0;//libera nova coleta     
        PIE1bits.CCP1IE = 1;//liga interrupcão do ccp1
        while(tempo_ccp1<100)//demora em media 100ms
         { 
           if(estado_amostras==3)
               {//tem coleta mostra o valor 
                   float temp;
                   unsigned int parte_inteira;                //declaração de variável local
                   unsigned int parte_decimal;                //declaração de variável local 
                   if(descarta_FP == 0)
                   {
                    temp=EQ_FP;//pois divide por (2*semicilo)
                   }
                   else
                   {
                    temp=0;//tensão ou corrente sem leitura   
                   }
                    parte_inteira = (int)temp;            //pega parte inteira do valor
                    parte_decimal =(unsigned int)((temp - parte_inteira) * 10);    //resgata parte fracionária do valor ,2 casa decimais
                    sprintf(Buffer_lcd,"%c%u.%u",sinal_FP,parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
                    impri_lcd(4,'3');
                    if(descarta_FP == 0)
                   {
                     temp=(EQ_FREQ);//para 20Mhz - divide por 8 pois o T1CON está assim
                     
                   }
                   else
                   {
                    temp=(EQ_FREQ*2);//tensão ou corrente sem leitura -> onda fica igual a corrente ou a tensão  
                   }                   
                   parte_inteira = (int)temp;            //pega parte inteira do valor
                   parte_decimal =(unsigned int)((temp - parte_inteira) * 10);    //resgata parte fracionária do valor ,2 casa decimais
                   sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
                   impri_lcd(3,'4'); //frequencia
                   _delay_ms(250);//lê a cada 0.5s
                   PIE1bits.CCP1IE = 1;//liga interrupcão do ccp1
                   _delay_us(10);//espera tempo para CCP1 se preparar
                   estado_amostras=0;//libera nova coleta
               }
               if(estado_amostras==2)
               {
                    if((sin2_FP==0)||(sin1_FP==0))//se os dois estiver em um indica que está na descida do sinal
                        //(do segundo sinal) e isso gera imprecisão 
                           {//sinal invalido  
                             estado_amostras = 0;
                           }

               }
           _delay_ms(1);
           tempo_ccp1++;
       }//fim do laçõ ccp1
       PIE1bits.CCP1IE = 0;//desliga interrupcão do ccp1 pra garantir
     }
     else//modo DC
     {
         Read_ADC_DC(); 
     }     
    _delay_ms(500);
    LED_STATUS=~LED_STATUS;
  }//fim do while 
  }//fim do laço main

/////////////////////////////////////////////////////////////////////////////////

void verifica_botao(void)
{
     if(!BOT_SEL)//testa botão de seleção -> AC/DC
     {
            _delay_ms(200);//debouce
            if(!BOT_SEL)
            {
               if(modo_tensao)
               {//modo DC
                   Tela_inicial('D');
                   modo_tensao=0;
                   LED_MODO =0;   
                   PIE1bits.CCP1IE = 0;//desliga interrupcão do ccp1
               }
               else
               {//modo AC
                   Tela_inicial('A');
                   LED_MODO =1;
                   modo_tensao=1;
                   estado_amostras=0;//libera nova coleta     
                   PIE1bits.CCP1IE = 1;//liga interrupcão do ccp1
               }
               _delay_ms(200);//debouce
            }
     }
}
 void Inic_Regs(void)
{
    TRISA = 0x03;            //PORTA saida, exceto RA0(tensão) e RA1(corrente) será usado para entradas analogicas
    TRISB = 0x00;            //PORTB saida
    TRISC = 0x34;            //PORTC saida e pino c2 entrada ccp1,pino C4 e C5 informa sinal do fator de potencia 
    TRISD = 0x00;            //PORTD saida
    TRISE = 0x04;            //PORTE saida,botão no RE2
    PORTA = 0x00;            //limpa PORTA
    PORTB = 0x00;            //limpa PORTB
    PORTC = 0x00;            //limpa PORTC
    PORTD = 0x00;            //limpa PORTD:
    PORTE = 0x00;            //limpa PORTE
//********************************************************************
    ADCON0 = 0X01;          //liga modulo analogico digital e configura entrada de sinal para o pino RA0(AN0)
    ADCON1=0b00001011;          // Vref- seta ao Vss e  Vref+ ao Vdd , somente Ra0 ate Ra4 analogico
    //ADCON2 = 0b101101010;    // justificado a direita     // clk Fosc/32
       ADCON2=0b10101001;
    // tempo de aquisição 12TAD(assim funciona pra qualquer cristal, porem fica mais lento, se precisar deve ser ajustado)
    INTCON=0b11000000;//liga interrupção de periferico e geral
    T3CON = 0;//mantem o ccp1 e ccp2 ligado ao timer1
    T1CON = 0b10110001;//modo de 16bits - fosc/dividido por 8 - timer ligado (lé no maximo500khz para clock de 8MHZ))
    CCP1CON = 0b00000101;//captura a cada borda de subida
    IPR1= 0X05;//coloca estouro de timer  e ccp1 como alta prioridade
    PIR1bits.CCP1IF = 0;//limpa flag do ccp1
    PIE1bits.CCP1IE = 1;//liga interrupcão do ccp1
    PIE1bits.TMR1IE = 0;//deixa interrupção de timer desligado(não vai prescisar))
 }
 //configura tela inicial
 void Tela_inicial(unsigned char modo)
{    
    if(modo == 'A') 
    {
        sprintf(Buffer_lcd,"Vrms=      V  -> AC ");
        impri_lcd(0,'1');
        sprintf(Buffer_lcd,"Vp=     V  Ip=     A");
    }else
    {
        sprintf(Buffer_lcd,"Multimetro    -> DC ");
        impri_lcd(0,'1');
        sprintf(Buffer_lcd,"Vdc=     V Idc=    A");  
    }
  impri_lcd(0,'2'); 
  sprintf(Buffer_lcd,"Fp=        Pi=     W");
  impri_lcd(0,'3');
  sprintf(Buffer_lcd,"F=      hz Pm=     W");
  impri_lcd(0,'4');  
} 
 //faz leitura de amostras em corrente continua
void Read_ADC_CA(void)
{       
       unsigned int lido_adc;
       float temp,tensao;
       unsigned int parte_inteira;                //declaração de variável local
       unsigned int parte_decimal;                //declaração de variável local
       unsigned int hist_leitura[5]={0,0,0,0,0};//pega 5 amostras para não estourar a variavel
       unsigned char fim_captura=245;//esse valor pede pra encontrar quando a onda está subindo
       unsigned int num_de_tentativas = 0;
       descarta_FP=0;//libera leitura do fator de potencia
       //////////Lê tensão////////////////////
        ADCON0 = 0X01; //RA0
        _delay_us(40);//espera tensão estabilizar 
        hist_leitura[0] = 0XFFFF;//coloca valor maximo para garantir 
        while((fim_captura == 245)&&(modo_tensao==1))
        {
            ADCON0bits.GO = 1; //RA1 habilita conversão         
             while (ADCON0bits.DONE == 1); //espera finalizar leitura
            hist_leitura[1] =  (((int)ADRESH)<<8)|(ADRESL);
             if((hist_leitura[1]>hist_leitura[0])&&(hist_leitura[1]<10))//pra garantir que está no começo da onda 
                {
                   fim_captura = 0;//finalizou processo onda está subindo
                }
            hist_leitura[0]= hist_leitura[1];//recebe valor atual
            verifica_botao();
            num_de_tentativas++;
            if(num_de_tentativas>1000)//anti travamento
            {
                fim_captura = 253;//indica que não leu tensão
            }
        }
        while((fim_captura < 250)&&(modo_tensao==1))
        {
            ADCON0bits.GO = 1; //RA1 habilita conversão         
            while (ADCON0bits.DONE == 1); //espera finalizar leitura
            hist_leitura[fim_captura] =  (((int)ADRESH)<<8)|(ADRESL);
            if(fim_captura == 0)
            {
                if(hist_leitura[0]<hist_leitura[4])
                {
                   fim_captura = 252;//finalizou processo 
                   lido_adc = ((hist_leitura[4]+hist_leitura[3]+hist_leitura[2])/3);//tira uma média das 3 ultimas amostras
                }
            }
            else
            {
                if(hist_leitura[fim_captura]<hist_leitura[fim_captura-1])
                {
                    unsigned char conta = 0;
                     lido_adc =0;
                    while(conta<5)//0 a 4
                    {
                        if(conta != fim_captura)//ignora valor atual
                        {
                            lido_adc =  lido_adc + hist_leitura[conta];
                        }
                        conta++;
                    }
                     lido_adc = lido_adc/4;//tira media
                    fim_captura = 252;//finalizou processo 
                }
            }            
            if(fim_captura<250)
            {
                fim_captura++;
                if(fim_captura > 4)
                {
                    fim_captura = 0; 
                }
            }
           verifica_botao(); 
        }
        if(fim_captura == 253)
        {
          temp = 0;
          descarta_FP=1;
        }else
        {
         temp = EQ_TENSAO ;         
        }
    //*******************************************************************
        if(temp < 0)                    //valor é menor que 0?
        { temp = temp * (-1);        //inverte o sinal de float
        }
    //*******************************************************************
        parte_inteira = (int)temp;            //pega parte inteira do valor
        tensao =temp;
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        Tela_inicial('A');//limpa campos
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(3,'2'); 
        temp=(tensao/1.4142);
        parte_inteira = (int)temp;            //pega parte inteira do valor        
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(5,'1'); 
        
        //////////Lê corrente////////////////////        
        hist_leitura[0]=0;
        hist_leitura[1]=0;
        hist_leitura[2]=0;
        hist_leitura[3]=0;
        hist_leitura[4]=0;
        ADCON0 = 0X05; ///liga RA1  
        _delay_us(40);//espera tensão estabilizar 
        fim_captura = 245;
        hist_leitura[0] = 0XFFFF;//coloca valor maximo para garantir 
        num_de_tentativas=0;
         while((fim_captura == 245)&&(modo_tensao==1))
        {
            ADCON0bits.GO = 1; //RA1 habilita conversão         
            while (ADCON0bits.DONE == 1); //espera finalizar leitura
            hist_leitura[1] =  (((unsigned int)ADRESH)<<8)|(ADRESL);
             if((hist_leitura[1]>hist_leitura[0])&&(hist_leitura[1]<10))//pra garantir que está no começo da onda
                {
                   fim_captura = 0;//finalizou processo onda está subindo
                }
            hist_leitura[0]= hist_leitura[1];//recebe valor atual
            verifica_botao();
            num_de_tentativas++;
            if(num_de_tentativas>1000)//anti travamento
            {
                fim_captura = 253;//indica que não leu tensão
            }            
        }
        while((fim_captura < 250)&&(modo_tensao==1))
        {
            ADCON0bits.GO = 1; //RA0 habilita conversão         
            while (ADCON0bits.DONE == 1); //espera finalizar leitura
            hist_leitura[fim_captura] =  (((int)ADRESH)<<8)|(ADRESL);
            if(fim_captura == 0)
            {
                if(hist_leitura[0]<hist_leitura[4])
                {
                   fim_captura = 252;//finalizou processo 
                   lido_adc = ((hist_leitura[4]+hist_leitura[3]+hist_leitura[2])/3);//tira uma média das 3 ultimas amostras
                }
            }
            else
            {
                if(hist_leitura[fim_captura]<hist_leitura[fim_captura-1])
                {
                    unsigned char conta = 0;
                     lido_adc =0;
                    while(conta<5)//0 a 4
                    {
                        if(conta != fim_captura)//ignora valor atual
                        {
                            lido_adc =  lido_adc + hist_leitura[conta];
                        }
                        conta++;
                    }
                     lido_adc = lido_adc/4;//tira media
                    fim_captura = 252;//finalizou processo 
                }
            }            
            if(fim_captura<250)
            {
                fim_captura++;
                if(fim_captura > 4)
                {
                    fim_captura = 0; 
                }
            }
            verifica_botao();
        }
        if(fim_captura == 253)
        {
          temp = 0;
          descarta_FP=1;
        }else
        {
         temp = EQ_CORRENTE;  
        }               
    //*******************************************************************
        if(temp < 0)                    //valor é menor que 0?
        { temp = temp * (-1);        //inverte o sinal de float
        }
    //*******************************************************************
        parte_inteira = (int)temp;            //pega parte inteira do valor        
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(14,'2');
        temp = tensao*temp;
        parte_inteira = (int)temp;            //pega parte inteira do valor 
        parte_decimal =(unsigned int)((temp - parte_inteira) * 10);//somente uma casa decimal
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(14,'3'); 
        //potencia media ac
        temp = temp/2;
        parte_inteira = (int)temp;            //pega parte inteira do valor 
        parte_decimal =(unsigned int)((temp - parte_inteira) * 10);//somente uma casa decimal
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(14,'4');

}
//faz leitura de amostras em corrente continua
void Read_ADC_DC(void)
{
       unsigned int lido_adc;
       float temp,tensao;
       unsigned int parte_inteira;                //declaração de variável local
       unsigned int parte_decimal;                //declaração de variável local
       unsigned int amostras[5];
       //////////Lê tensão////////////////////
        ADCON0 = 0X01;//RA0 
        _delay_us(40);//espera tensão estabilizar
        lido_adc = 0;
        while(lido_adc<5)
        {
        ADCON0bits.GO = 1; //RA0
        while (ADCON0bits.DONE == 1); //espera finalizar leitura
        amostras[lido_adc] =  (((unsigned int)ADRESH)<<8)|(ADRESL);
        lido_adc++;
        }
        temp = amostras[0] +amostras[1] +amostras[2] +amostras[3] +amostras[4];
        lido_adc =(unsigned int)temp/5;
        temp = EQ_TENSAO;  
        Tela_inicial('D');//limpa campos
    //*******************************************************************
        if(temp < 0)                    //valor é menor que 0?
        { temp = temp * (-1);        //inverte o sinal de float
        }
    //*******************************************************************
        parte_inteira = (int)temp;            //pega parte inteira do valor
        tensao =temp;
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(4,'2');
        
        //////////Lê corrente ////////////////////
       ADCON0 = 0X05; ///liga RA1        
        _delay_us(40);//espera tensão estabilizar
       lido_adc = 0;
        while(lido_adc<5)
        {
        ADCON0bits.GO = 1; //RA0
        while (ADCON0bits.DONE == 1); //espera finalizar leitura
        amostras[lido_adc] =  (((unsigned int)ADRESH)<<8)|(ADRESL);
        lido_adc++;
        }
        temp = amostras[0] +amostras[1] +amostras[2] +amostras[3] +amostras[4];
        lido_adc =(unsigned int)temp/5;
        temp = EQ_CORRENTE;       
    //*******************************************************************
        if(temp < 0)                    //valor é menor que 0?
        { temp = temp * (-1);        //inverte o sinal de float
        }
    //*******************************************************************
        parte_inteira = (int)temp;            //pega parte inteira do valor        
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(15,'2');
        temp = tensao*temp;
        parte_inteira = (int)temp;            //pega parte inteira do valor 
        parte_decimal =(unsigned int)((temp - parte_inteira) * 10);//somente uma casa decimal
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(14,'3');        
        PM[3]= PM[4];
        PM[2]= PM[3];
        PM[1]= PM[2];
        PM[0]= PM[1];
        PM[4]= temp+ PM[0]+PM[1]+ PM[2]+ PM[3];
        tensao =temp;
        temp= PM[4]/5;//potencia media reaproveitando variavel
        PM[4]=tensao;
        parte_inteira = (int)temp;            //pega parte inteira do valor 
        parte_decimal =(unsigned int)((temp - parte_inteira) * 10);//somente uma casa decimal
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(14,'4');
}

Arquivo MCC18_18F4550_H.h


/* 
 * File:   main_lcd.c
 * Author: aguivone
 * descrição: como usar display lcd no modo 4 bits
 * freq.de clock: 20MHZ
 * data:31/10/2019 
 * não esquecer de coloca o arquivo MCC18_18F4550.h
 */
#include <stdio.h>
#include <p18f4550.h>    
#include <string.h>
#include <delays.h>

#ifndef __MCC18_18F4550___H
#define    __MCC18_18F4550___H

// CONFIG1L
#pragma config PLLDIV = 1       // PLL Prescaler Selection bits (No prescale (4 MHz oscillator input drives PLL directly))
#pragma config CPUDIV = OSC1_PLL2// System Clock Postscaler Selection bits ([Primary Oscillator Src: /1][96 MHz PLL Src: /2])
#pragma config USBDIV = 1       // USB Clock Selection bit (used in Full-Speed USB mode only; UCFG:FSEN = 1) (USB clock source comes directly from the primary oscillator block with no postscale)

// CONFIG1H
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator (HS))
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

// CONFIG2L
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOR = OFF        // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)
#pragma config BORV = 3         // Brown-out Reset Voltage bits (Minimum setting 2.05V)
#pragma config VREGEN = OFF     // USB Voltage Regulator Enable bit (USB voltage regulator disabled)

// CONFIG2H
#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config CCP2MX = ON      // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = ON      // PORTB A/D Enable bit (PORTB<4:0> pins are configured as analog input channels on Reset)
#pragma config LPT1OSC = OFF    // Low-Power Timer 1 Oscillator Enable bit (Timer1 configured for higher power operation)
#pragma config MCLRE = ON       // MCLR Pin Enable bit (MCLR pin enabled; RE3 input pin disabled)

// CONFIG4L
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = OFF        // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
#pragma config ICPRT = OFF      // Dedicated In-Circuit Debug/Programming Port (ICPORT) Enable bit (ICPORT disabled)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection bit (Block 0 (000800-001FFFh) is not code-protected)
#pragma config CP1 = OFF        // Code Protection bit (Block 1 (002000-003FFFh) is not code-protected)
#pragma config CP2 = OFF        // Code Protection bit (Block 2 (004000-005FFFh) is not code-protected)
#pragma config CP3 = OFF        // Code Protection bit (Block 3 (006000-007FFFh) is not code-protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block (000000-0007FFh) is not code-protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM is not code-protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection bit (Block 0 (000800-001FFFh) is not write-protected)
#pragma config WRT1 = OFF       // Write Protection bit (Block 1 (002000-003FFFh) is not write-protected)
#pragma config WRT2 = OFF       // Write Protection bit (Block 2 (004000-005FFFh) is not write-protected)
#pragma config WRT3 = OFF       // Write Protection bit (Block 3 (006000-007FFFh) is not write-protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) are not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot block (000000-0007FFh) is not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM is not write-protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 (000800-001FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 (002000-003FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR2 = OFF      // Table Read Protection bit (Block 2 (004000-005FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR3 = OFF      // Table Read Protection bit (Block 3 (006000-007FFFh) is not protected from table reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot block (000000-0007FFh) is not protected from table reads executed in other blocks)


#endif    /* __MCC18_18F4550___H */

Arquivo tempo.h
/* 
 * File:   tempo.h
 * Author: aguiv
 *
 * Created on 8 de Novembro de 2019, 15:54
 */
#include <delays.h>
#ifndef TEMPO_H
#define    TEMPO_H

 void _delay_us(int valor);
 void _delay_ms(int valor);
 #include "tempo_20MHz.c"

#endif    /* TEMPO_H */

Aquivo tempo_20MHz.c
/* 
 * File:   tempo.c
 * Author: aguivone
 *
 * Created on 8 de Novembro de 2019, 16:02
 */
// usado para criar funções de tempo

 void _delay_us(int valor)
 {
     while(valor>0)
     {
      Nop();Nop();Nop();Nop();Nop();//Nop();Nop();Nop();      
      //para 32-> 8 ciclos ,20MHZ -> 5 ciclos dão 1us , para 8mhz -> 2ciclo, para 4mhz ->>1ciclo 
      valor--;
     }
 }
 void _delay_ms(int valor)
 {
     while(valor>0)
     {
      Delay1KTCYx(5);
      //para 32MHZ -> coloque 8
      //para 20MHZ -> 5000 ciclos dão 1ms -> coloque 5
      // 8mhz -> 2000 -> coloque 2
      // 4mhz -> 1000 -> coloque 1
      valor--;
     }
 }
 //delay de segundos
 void _delay_s(int valor)
 {
     while(valor>0)
     {
      _delay_ms(1000); 
      valor--;
     }
 }



Aquivo da biblioteca do LCD - LCD_4bits.h

/* 
 * File:   LCD_4bits.h
 * Author: aguivone
 *
 * Created on 8 de Novembro de 2019, 16:07
 */
//biblioteca para acessar o display de LCD no modo 4 bits
//o pino r/w é conectado ao terra pois não é feita nenhuma leitura no display
//configuração:
//
// pino display  / pino da porta selecionada
//
// D4 = bit 0 da porta
// D5 = bit 1 da porta
// D6 = bit 2 da porta
// D7 = bit 3 da porta
//
// RS = bit 4 da porta
// EN = bit 5 da porta
//

#include <stdio.h>
#include <p18f4550.h>    //se for necessario troque este
#include <string.h>

#ifndef LCD_4BITS_H
#define    LCD_4BITS_H
//variaveis locais
extern unsigned char Buffer_lcd[16]; //lembre de criar esta variavel no codigo principal
/////////////////////////////////////pinos do microcontrolador usado no lcd ///////////////////////////////////////////
//define qual porta vai usar neste caso port D

///acepic////////////
/*
  #define BIT_0  PORTDbits.RD4
 #define BIT_1  PORTDbits.RD5
 #define BIT_2  PORTDbits.RD6
 #define BIT_3  PORTDbits.RD7

 #define RS     PORTEbits.RE0
 #define EN     PORTEbits.RE1
*/
//prototipo montado na protoboard/////////
 #define BIT_0  PORTDbits.RD4
 #define BIT_1  PORTDbits.RD5
 #define BIT_2  PORTDbits.RD6
 #define BIT_3  PORTDbits.RD7

 #define RS     PORTDbits.RD0
 #define EN     PORTDbits.RD1


union{
    unsigned char VALOR;
    struct{
        unsigned b0:1;
        unsigned b1:1; 
        unsigned b2:1;
        unsigned b3:1;
        unsigned b4:1;
        unsigned b5:1;
        unsigned b6:1;
        unsigned b7:1;
    };    
}LCD;
void porta_lcd(unsigned char ucDado); 
void escreve_LCD(unsigned char ucDado,unsigned char ucVlr); 
void inicializa_LCD(void);
void impri_lcd(int iPos_inicial ,unsigned char ucLinha);
void impri_caracter_lcd(unsigned char ucCaracter,int iPos_inicial ,unsigned char ucLinha);
 #include "LCD_4bits.c"
#endif    /* LCD_4BITS_H */



Até a próxima! obrigado!

Usando lcd no modo 4 bits, Wattímetro,voltímetro e amperímetro.


     Olá pessoal que acompanha o blog!
   Neste exemplo os conceitos abordados, no uso do compilador C18, serão o uso do conversor analógico digital e a implementação da comunicação em 4 bits utilizando LCD(funciona para 16X2 ou 4X20). A intenção é criar wattímetro que por consequência deve ler tensão e corrente, lembre-se que este circuito serve apenas para corrente continua. 

O código fonte :


/* 
 * File:   main_lcd.c
 * Author: aguivone
 * descrição: como usar display lcd no modo 4 bits no compilador C18
 * freq.de clock: 20MHZ
 * data:08/12/2019 
 */
#include <stdio.h>
#include <p18f4550.h>    
#include <string.h>
#include <delays.h>

//variaveis locais
unsigned char Buffer_lcd[16];
/////////////////////////////////////pinos do microcontrolador usado no lcd ///////////////////////////////////////////
//o pino r/w é conectado ao terra pois não é feita nenhuma leitura no display
//configuração:
//
// pino display  / pino da porta selecionada
//
// D4 = bit 0 da porta
// D5 = bit 1 da porta
// D6 = bit 2 da porta
// D7 = bit 3 da porta
//
// RS = bit 4 da porta
// EN = bit 5 da porta
//
//define qual porta vai usar neste caso port D
 #define BIT_0  PORTDbits.RD0
 #define BIT_1  PORTDbits.RD1
 #define BIT_2  PORTDbits.RD2
 #define BIT_3  PORTDbits.RD3

 #define RS     PORTDbits.RD4
 #define EN     PORTDbits.RD5
//////////////usado para escrever em cada bit da porta com o lcd.
union{
    unsigned char VALOR;
    struct{
        unsigned b0:1;
        unsigned b1:1; 
        unsigned b2:1;
        unsigned b3:1;
        unsigned b4:1;
        unsigned b5:1;
        unsigned b6:1;
        unsigned b7:1;
    };    
}LCD;
////prototipos de funções //////////////////////////////////////////////////////////////////
void Inic_Regs (void);
void _delay_us(int valor);
void _delay_ms(int valor);
unsigned char valor_bit(char cDa,char cBit);
void porta_lcd(unsigned char ucDado); 
void escreve_LCD(unsigned char ucDado,unsigned char ucVlr); 
void inicializa_LCD(void);
void impri_lcd(int iPos_inicial ,unsigned char ucLinha);
void impri_caracter_lcd(unsigned char ucCaracter,int iPos_inicial ,unsigned char ucLinha);
void Read_ADC (void);

////////função principal /////////////////////////////////////////////////////////////////////

void main() 
{
  Inic_Regs();
  inicializa_LCD();
  sprintf(Buffer_lcd,"Volts Corr. Pot.");
  impri_lcd(0,'1');
  sprintf(Buffer_lcd,"  0     0    0  ");
  impri_lcd(0,'2'); 
  while(1)
   { 
         Read_ADC();
         _delay_ms(300);//lê a cada 2s
   }//fim do laçõ while
 
  }//fim do laço main
/////////////inicializa as portas ////////////////////////////////////// 
void Inic_Regs(void)
{
    TRISA = 0x03;            //PORTA saida, exceto RA0(tensão) e RA1(corrente) será usado para entradas analogicas
    TRISB = 0x00;            //PORTB saida
    TRISC = 0x00;            //PORTC saida
    TRISD = 0x00;            //PORTD saida
    TRISE = 0x00;            //PORTE saida
    ADCON0 = 0X01;          //liga modulo analogico digital e configura entrada de sinal para o pino RA0(AN0)
    ADCON1=0b00001110;          // Vref- seta ao Vss e  Vref+ ao Vdd , somente Ra0 e Ra1 analogico
    //ADCON2 = 0b101101010;    // justificado a direita     // clk Fosc/32
       ADCON2=0b10101001;
    // tempo de aquisição 12TAD(assim funciona pra qualquer cristal, porem fica mais lento, se precisar deve ser ajustado)
    PORTA = 0x00;            //limpa PORTA
    PORTB = 0x00;            //limpa PORTB
    PORTC = 0x00;            //limpa PORTC
    PORTD = 0x00;            //limpa PORTD:
    PORTE = 0x00;            //limpa PORTE
//********************************************************************
}
//////////////funções de tempo /////////////////////////////////////////////////////////////// 
//===================================================================================
//Função:     _delay_us())
//Parâmetros: inteiro
//Retorno:   não tem retorno.
//Descrição: perde tempo em microsegundoas.
//===================================================================================
 void _delay_us(int valor)
 {
     while(valor>0)
     {
      Nop();Nop();Nop();Nop();Nop();//para 20MHZ -> 5 ciclos dão 1us//  para 8mhz -> 0,5 -> 2 ciclos
      valor--;
     }
 }
 //===================================================================================
//Função:     _delay_ms())
//Parâmetros: inteiro
//Retorno:   não tem retorno.
//Descrição: perde tempo em milisegundos.
//===================================================================================
 void _delay_ms(int valor)
 {
     while(valor>0)
     {
      Delay1KTCYx(5);//para 20MHZ -> 5000 ciclos dão 1ms -> [5]// 8mhz -> 2000 -> [2].
      valor--;
     }
 }

//===================================================================================
//Função:     _porta_lcd
//Parâmetros: char cDado
//Retorno:   não tem retorno.
//Descrição: envia um comando de configuração veja que os tempos devem ser maiores .
//===================================================================================
void porta_lcd(unsigned char ucDado)//byte a ser enviado
{
       LCD.VALOR = ucDado;
       //envia os bytes mais altos
       RS = 0; //habilita comandos
       BIT_0 = LCD.b4;
       BIT_1 = LCD.b5;
       BIT_2 = LCD.b6;
       BIT_3 = LCD.b7;
       EN = 1;
       _delay_ms(15);
       EN = 0;
       _delay_ms(15);
       //envia os bytes mais baixos
       BIT_0 = LCD.b0;
       BIT_1 = LCD.b1;
       BIT_2 = LCD.b2;
       BIT_3 = LCD.b3;
       EN = 1;
       _delay_ms(15);
       EN = 0;
       _delay_ms(15);
}
//===================================================================================
//Função:     _escreve_LCD
//Parâmetros: char cDado,
//            int iVlr
//Retorno:   não tem retorno.
//Descrição: escreve um caracter no display ou envia um comando.
//===================================================================================
void escreve_LCD(unsigned char ucDado,unsigned char ucVlr)//byte a ser enviado ,escreve caracter(0) ou comando(1)
{
    if(ucVlr == '0')
       {
         RS = 0; //habilita comandos
       }
       else
       {
        RS = 1; //habilita dados
       }
       LCD.VALOR = ucDado;
        _delay_us(5);//tempo para display atualizar
       //envia os bytes mais altos
       BIT_0 = LCD.b4;
       BIT_1 = LCD.b5;
       BIT_2 = LCD.b6;
       BIT_3 = LCD.b7;
       EN = 1;
       _delay_us(50);//tempo estabilizar o pino
       EN = 0;
       _delay_us(50);//tempo estabilizar o pino
       //envia os bytes mais baixos
       BIT_0 = LCD.b0;
       BIT_1 = LCD.b1;
       BIT_2 = LCD.b2;
       BIT_3 = LCD.b3;
       EN = 1;
       _delay_us(50);//tempo estabilizar o pino
       EN = 0;
       _delay_us(200);
}
//===================================================================================
//Função:     _inicializa_LCD
//Parâmetros: não tem
//Retorno:   não tem retorno.
//Descrição: faz a configuração inicial do display, verifique os datasheet dos fabricantes.
//===================================================================================
void inicializa_LCD(void)
{
       EN = 0;
       _delay_ms(15);//tempo para estabilizar a tensão e preparar o display
       porta_lcd(0X02);//cursor home - posiciona no inicio
       porta_lcd(0X28);//configura para usar modo 4bits,manda usar 2 linhas e matriza de caracter de 5X7.
       porta_lcd(0X01);//limpa display
       porta_lcd(0X06);//deslocamento do cursor para direita ,sem deslocamento de caracter automaticamente. 
       porta_lcd(0X0C);//liga display :0XC0 -> cursor desligado, 0X0F ->intermitente,0E -> fixo ,08 -> desligado.
       RS = 1; //habilita dados        

}
//===================================================================================
//Função:    _impri_caracter_lcd
//Parâmetros: char cCaracter, -> caracter a ser escrito
//            int iPos_inicial, indica em qual posição deve  colocar o caracter
//            int iLinha, indica qual o a linha do display vai usar
//Retorno:   não tem retorno.
//Descrição: usada para escrever um caracter em um display de lcd.
//===================================================================================
void impri_caracter_lcd(unsigned char ucCaracter,int iPos_inicial ,unsigned char ucLinha)
{
      switch(ucLinha)
           {
            case '1' :{escreve_LCD(0X80 + iPos_inicial,'0');}break;//linha1
            case '2' :{escreve_LCD(0XC0 + iPos_inicial,'0');}break;//linha2
            case '3' :{escreve_LCD(0X94 + iPos_inicial,'0');}break;//linha3
            case '4' :{escreve_LCD(0XD4 + iPos_inicial,'0');}//linha4
            }
       escreve_LCD(ucCaracter,'1');
}
 

//===================================================================================
//Função:    _impri_lcd
//Parâmetros: const char *ccFrase, mensagem que deve ser escrita usando "(aspas) para delimitar o tamanho
//            int iPos_inicial, indica em qual posição deve começar a colocar a mensagem
//            int iLinha, indica qual o a linha do display vai usar
//Retorno:   não tem retorno.
//Descrição: usada para escrever uma string(vetor de caracteres) em um display de lcd com parametro string.
//===================================================================================
//void impri_lcd(unsigned char frase[],int iPos_inicial ,unsigned char ucLinha)//  cuidado para não exceder a quantidade de caracter
void impri_lcd(int iPos_inicial ,unsigned char ucLinha)
{
      unsigned int indice=0;
      unsigned int tamanho = strlen(Buffer_lcd);
      switch(ucLinha)
           {
            case '1' :{escreve_LCD(0X80 + iPos_inicial,'0');}break;//linha1
            case '2' :{escreve_LCD(0XC0 + iPos_inicial,'0');}break;//linha2
            case '3' :{escreve_LCD(0X94 + iPos_inicial,'0');}break;//linha3
            case '4' :{escreve_LCD(0XD4 + iPos_inicial,'0');}//linha4
            }
      while(indice<tamanho)
       {
          if(Buffer_lcd[indice]!='\0')
          {           
            if((Buffer_lcd[indice]!='"') && (indice<21))//dados
                        {
                          escreve_LCD(Buffer_lcd[indice],'1');
                        }
          }
          else
          {
              indice = tamanho;//finaliza impressão
          }
       indice++;
       }
}

void Read_ADC (void)
{
       int lido_adc;
       float temp,tensao;
       unsigned int parte_inteira;                //declaração de variável local
       unsigned int parte_decimal;                //declaração de variável local
       //////////Lê tensão////////////////////
        ADCON0 = 0X05; 
        _delay_us(40);//espera tensão estabilizar
        ADCON0bits.GO = 1; //RA1
        while (ADCON0bits.DONE == 1); //espera finalizar leitura
       // lido_adc = ((((int)ADRESH <<8)|(ADRESL))&0x03FF); //Lê os valores em hexa
        lido_adc =  (((int)ADRESH)<<8)|(ADRESL);
        temp = (float)((lido_adc*5.0)/1023);       
    //*******************************************************************
        if(temp < 0)                    //valor é menor que 0?
        { temp = temp * (-1);        //inverte o sinal de float
        }
    //*******************************************************************
        parte_inteira = (int)temp;            //pega parte inteira do valor
        tensao =temp;
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(0,'2');
        
        //////////Lê corrente ////////////////////
       ADCON0 = 0X01; ///liga RA0        
        _delay_us(40);//espera tensão estabilizar
        ADCON0bits.GO = 1; //RA1
        while (ADCON0bits.DONE == 1); //espera finalizar leitura
       // lido_adc = ((((int)ADRESH <<8)|(ADRESL))&0x03FF); //Lê os valores em hexa
        lido_adc =  (((int)ADRESH)<<8)|(ADRESL);
        temp = (float)((lido_adc*5.0)/1023);       
    //*******************************************************************
        if(temp < 0)                    //valor é menor que 0?
        { temp = temp * (-1);        //inverte o sinal de float
        }
    //*******************************************************************
        parte_inteira = (int)temp;            //pega parte inteira do valor        
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(6,'2');
        temp = tensao*temp;
        parte_inteira = (int)temp;            //pega parte inteira do valor 
        parte_decimal =(unsigned int)((temp - parte_inteira) * 10);//somente uma casa decimal
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(12,'2');
}


A simulação do proteus:


     Muito obrigado,e até a próxima!

quinta-feira, 31 de janeiro de 2019

PWM de 20KHZ com o PIC 12F683 - Saída variável de 0 a 10V.


          Olá pessoal que acompanha o blog! depois de um bom tempo sem nenhum post, temos mais um projeto realizado por nosso colaborador Renato Lôbo, que nos traz um circuito que controla a saída por meio do uso da modulação PWM.
           O funcionamento se baseia na leitura da tensão que vem do potenciômetro(que neste caso varia de 0 a 5V) e então o microcontrolador irá gerar um sinal de pwm na frequência de 20Khz  e com largura de pulso proporcional a entrada(ou seja para  0 a 5V temos 0 a 10V) veja Figura 1, O filtro passa baixas  na saída(R3 e C2) faz com que o sinal de onda quadrada se aproxime do sinal analógico, dando uma "suavizada" no contorno do sinal  que tera um ganho de potencia por meio do transistor BC337.

Figura 1 - sinal gerado pelo microcontrolador.

 O código fonte.

Paste your////////////////////////////////////////////////////////////////////////////////
//              Projeto: PWM de 20Khz                                         //
//                 Nome: Renato Lôbo Rodrigues                                //
//                 Data: 27/01/19                                             //
//                Email: lobosoft@oi.com.br                                   //
////////////////////////////////////////////////////////////////////////////////
#include <12F683.h>
#use delay (clock = 8Mhz)
//Configure os fusíveis em acordo com o pic selecionado
#fuses NOWDT
#fuses PUT
#fuses NOBROWNOUT
#fuses NOMCLR
#fuses INTRC_IO
#fuses PROTECT

//Estes registros encontra-se nas funções especiais de cada modelo de pic.
#bit tris_bot1 = 0x85.0 //direção do pino A0
#bit bot1      = 0x05.0 //pino A0

#bit tris_bot2 = 0x85.1 //direção do pino A1
#bit bot2      = 0x05.1 //pino A1

#bit tris_pwm = 0x85.2 //direção do pino A2
#bit p_pwm    = 0x05.2 //pino A2

#bit tris_led = 0x85.5 //direção do pino A5
#bit led      = 0x05.5 //pino A5

#bit tris_adc = 0x85.4 //direção do pino A4
#bit adc      = 0x05.4 //pino A5

//Registro dos comparadores
#byte CMCON0            = 0x19          //Controle dos comparadores

//Registros do ADC
#byte ADCON0            = 0x1F          //Controle do ADC
#bit  ADCON0_GO_DONE    = ADCON0.1      //Status de conversão
#byte ANSEL             = 0x9F          //Selecioa os pinos Analógicos
#byte ADRESH            = 0x1E          //endereço do byte mais significativo
#byte ADRESL            = 0x9E          //endereço do byte menos significativo


//Registros de interrupção
#byte INTCON            = 0x0B          //Controle de interrupção
#bit  INTCON_GIE        = INTCON.7      //Registro geral
//#bit  INTCON_PEIE       = INTCON.6    //Interrupção de periferico

//Registros de PULL_UP
#byte OPTION_REG        = 0x81          //Registros de Opção 
#bit  OPT_REG_GPPU      = OPTION_REG.7  //habilitar ou desabilitar os Pull_up dos pinos 
#byte WPU               = 0x95          //Configurar os pinos que vão ser habilitados

//Registros do CCP1
#byte CCP1CON           = 0x15          //Registro do CCP1
//#bit  CCP1CON_DC1B1     = CCP1CON.5     //Bit 1 para byte menos significativo
//#bit  CCP1CON_DC1B0     = CCP1CON.4     //Bit 0 para byte menos significativo
#byte CCPR1L            = 0x13          //Byte mais significativo

//Registros do Timer2 para PWM
#byte T2CON             = 0x12          //Controle do timer 2
#byte PR2               = 0x92          //periodo do PWM


int16 pwm = 0;

//______________________________________________________________________________
//Rotina principal
void main()
{
   int1  st1         = false;    //status do botão
   int1  st2         = false;    //status do botão
   int   toc         = false;    //inicia regulando no potenciometro
   int16 valor_adc   = 0;        //inicia com zero 
   
   tris_bot1         = 1;        //pino A0 como entrada
   tris_bot2         = 1;        //pino A1 como entrada
   tris_adc          = 1;        //pino A4 entrada analogica
   tris_pwm          = 0;        //pino A2 do pwm como saída
   pwm               = 0;        //inicia o pwm com zero
   tris_led          = 0;        //pino A5 do led como saída
   led               = 0;        //led apagado
   
   OPT_REG_GPPU   = 0;  //zero = habilita os pull-up 
   WPU            = 3;  //00000011 liga pul-up dos pinos A0 e A1
                        //||||||||_Pino A0
                        //|||||||_Pino A1
                        //||||||_Pino A2
                        //|||||_Não usado, pino A3 não tem Pull_up interno
                        //||||_Pino A4 
                        //|||_Pino A5
                        //||_dois bit não usado

   CMCON0     = 7;      //000000111 Comparadores Desligados
                        //|||||||||_Tres bits "um" desliga todos os comparador
                        //||||||_Não usado para esse caso
   
   ADCON0     = 141;    //10001101 Controle do ADC
                        //||||||||_liga ou desliga o ADC
                        //|||||||_Status de conversão, zero terminou a conversão 
                        //||||||_Tres bits para determminar o canal usado 
                        //|||_Não implementado
                        //||_VDD
                        //|_Formatado a direita
                        
   ANSEL       = 88;    //10001000  frequência de atualização e pino A4 como ADC
                        //||||||||_Quatro bit seta o pinos analogicos
                        //||||_Tres bits seta a frequencia de conversão
                        //|_Não usado

   INTCON_GIE  = 1;     //Habilita Registro Geral
   
   CCP1CON     = 12;    //00001100 Habilita o modo PWM 
                        //||||||||_Quatro bits define o uso do PWM do CCP1
                        //||||_Dois bit do Duty
                        //||_Não usado
                        
   CCPR1L      = 0;     //Byte mais significativo
 
   T2CON       = 4;     //00000100
                        //||||||||_2 bit prescaler do time 2 = 1
                        //||||||_ bit liga o timer2
                        //|||||_Quatro bit do Postscaler = 1
                        //|_não usado
                        
   PR2         = 49;    //49 calculado para PWM com frequência de 20 Khz


   //Setando o Duty
   //CCPR1L = pwm>>2;   //DUTY(;
   //CCP1CON = (pwm <<4) | 12;

   while(true)     //laço infinito
   {
      ADCON0_GO_DONE = 1;
      while(ADCON0_GO_DONE)//Espera até terminar a conversão analogica para digital  
      valor_adc = ((int16)ADRESH<<8)|(ADRESL); //Formatado a direita
      
      if(!toc) pwm =  valor_adc/5.0895;   //1023/201

      CCPR1L = pwm>>2;                    //Duty
      //CCP1CON = (pwm<<4) | 12;      //dois bits do Duty    
      CCP1CON = (pwm<<4) | 12;
      
      if ( bot1)   st1=false;
      if (!bot1 && st1==false)
      {
           st1=true;
           led = 1 ; //Acende o led1
           if(pwm>=10) pwm=pwm-10;
           if(pwm<=10)pwm=0;
           CCPR1L = pwm>>2;               //Duty
           CCP1CON = (pwm <<4) | 12; //dois bits do Duty            
           toc = true;
           delay_ms(10);
           led = 0 ;
      }
      if ( bot2)   st2=false;
      if (!bot2 && st2==false)
      {
           st2=true;
           led = 1 ; //Acende o led1
           if(pwm<191) pwm=pwm+10;
           if(pwm>=191)pwm=201;
           CCPR1L = pwm>>2;               //Duty
           CCP1CON = (pwm<<4) | 12; //dois bits do Duty          
           delay_ms(10);
           led = 0 ;
      }
      if(!bot1 && !bot2) toc = false;
   }
} text here.

Esquemático do projeto:


PCB - do protótipo.
              O protótipo final ficou conforme a figura a seguir:


Obrigado e até a próxima!

quarta-feira, 7 de fevereiro de 2018

NODEMCU+MQTT+ANDROID = IOT (PARTE2)


        Olá, pessoal que acompanha o blog lhes trago uma versão melhorada do ultimo exemplo postado, para isto organizei os componentes na protoboard e coloquei um botão, e um sensor LM35 para medir temperatura.Veja abaixo como ficou o novo protótipo.
//colocar foto
         Apesar do LM35 funcionar 4 a 30V(segundo o datasheet) com 3,3V funcionou bem(pelo menos pra medir temperatura ambiente,assim não precisou de nenhum circuito externo além do sensor.
         Usei um botão do tipo NA de forma que a entrada selecionada ficasse o tempo todo em nível alto indo para nível baixo quando pressionado.
        Adicionei mais um led(led amarelo) para indicar que o programa está rodando.Note que é possível monitorar pela porta serial tudo que acontece(caso queira).

O Código fonte :
//Autor: Aguivone    -  microcontroladores-C
//Descricao: Placa para acionar 4 reles usado um servidor MQTT.
//Data: 18/01/18
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <ESP8266WiFi.h>
#include <PubSubClient.h> // essa lib deve ser adicionada em seu programa do arduino
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//variaveis usadas na inicialização da wifi
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const char* ssid = "nome da wifi"; //Aqui o nome da sua rede local wifi
const char* password =  "senha"; // Aqui a senha da sua rede local wifi
const char* mqttServer = "algumacoisa.cloudmqtt.com"; // Aqui o endereço do seu servidor fornecido pelo site 
const int mqttPort =16000; // Aqui mude para sua porta fornecida pelo site
const char* mqttUser = "xxxxxx"; //  Aqui o nome de usuario fornecido pelo site
const char* mqttPassword = "hfjuytsgeh"; //  Aqui sua senha fornecida pelo site
unsigned long  tempo_rot = 1000;// aprox 1s
unsigned int est_ant_bot = 0;//guarda estado do botão pra não enviar a todo momento uma nova informação
WiFiClient espClient;
PubSubClient client(espClient);
/*
Equivalencia das saidas Digitais entre nodeMCU e ESP8266 (na IDE do Arduino)
-------------------------------------------------
NodeMCU / ESP8266  |  NodeMCU / ESP8266  |
D0 = 16            |  D6 = 12            |
D1 = 5             |  D7 = 13            |
D2 = 4             |  D8 = 15            |
D3 = 0             |  D9 = 3             |
D4 = 2             |  D10 = 1            |
D5 = 14            |                     |
-------------------------------------------------
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Definindo o nome dos pinos
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const int  LED_STATUS = 12; // led de status 
const int TEMP_LM35 = A0; // porta analogica
//////////////configura os pinos dos reles/////////////////////////////
const int RELE1 = 0; 
const int RELE2 = 4;
const int LED1 = 5;
const int LED2 = 14;
const int BOT  = 13;//botão
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//variaveis globais
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void mqtt_callback(char* topic, byte* dados_tcp, unsigned int length);///dados_tcp = payload


void setup() { 
  pinMode(RELE1, OUTPUT);
  pinMode(RELE2, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED_STATUS, OUTPUT);
  pinMode(BOT, INPUT);
  digitalWrite(RELE1,LOW);//desliga todos os reles
  digitalWrite(RELE2,LOW);
  digitalWrite(LED1,LOW);
  digitalWrite(LED2,LOW);
 
  Serial.begin(115200);
 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) 
  {    //inicialmente pisca o led da placa do nodemcu(é a mesma do ESP8266).
     delay(100);
    Serial.println("Conectando a WiFi..");
  }
  Serial.println("Conectado!"); 
  client.setServer(mqttServer, mqttPort);//configura servidor com as informações passadas
  client.setCallback(callback);
 
  while (!client.connected()) {
    Serial.println("Conectando ao servidor MQTT...");
    
    if (client.connect("Placa_leds", mqttUser, mqttPassword ))//colocar aqui o nome do dispositivo cliente("Placa_leds")
    {
 
      Serial.println("Conectado ao servidor MQTT!");  
      digitalWrite(LED_STATUS,HIGH); 
 
    } else {
 
      Serial.print("Falha ao conectar ");
      digitalWrite(LED_STATUS,LOW);
      Serial.print(client.state());
      delay(2000);
 
    }
  }
  
  client.publish("Status - placa_rele","Reiniciado!");
  client.publish("Placa_reles","Em funcionamento!");
  client.subscribe("RELE"); ///envia autorização para trabalhar com os topicos abaixo
  client.subscribe("LED"); 
  client.subscribe("TEMP"); 
  client.subscribe("BOT"); 
}
 //////////////////
void callback(char* topic, byte* dados_tcp, unsigned int length) 
{
   String msg ; // cria variavel que será responsavel pelo tratamento das informações
   for(int i = 0; i < length; i++) //converte array de bytes em string
    {  char c = (char)dados_tcp[i];
       msg += c;
    } 
   digitalWrite(LED_STATUS,LOW);
   delay(100);   
   digitalWrite(LED_STATUS,HIGH); //só pra indicar que houve recepção 
   Serial.print("\n\r topico:");//sempre será o escolhido no subscribe
   Serial.print(topic);   
   Serial.print("\n\r Pacote recebido:");
   Serial.print(msg);
   if (strcmp(topic, "RELE") == 0)//recebeu comando para trabalhar com o dispositivo relé
   {
    for (int i = 0; i < length; i++) // Varre o vetor(array) inteiro do pacote recebido 
        {         
          //toma ação dependendo da string recebida:      
      
          if (msg.equals("L1"))
          {
              digitalWrite(RELE1, HIGH);
              
          }
          if (msg.equals("D1"))
          {
              digitalWrite(RELE1, LOW);
            
          }
          if (msg.equals("L2"))
          {
              digitalWrite(RELE2, HIGH);
             
          }
          if (msg.equals("D2"))
          {
              digitalWrite(RELE2, LOW);
              
          }
        }
   }
   if (strcmp(topic, "LED") == 0)//recebeu comando para trabalhar com o dispositivo led
   {
       for (int i = 0; i < length; i++) // Varre o vetor(array) inteiro do pacote recebido 
        {         
          //toma ação dependendo da string recebida:      
      
          if (msg.equals("L1"))
          {
              digitalWrite(LED1, HIGH);
             
          }
          if (msg.equals("D1"))
          {
              digitalWrite(LED1, LOW);
              
          }
          if (msg.equals("L2"))
          {
              digitalWrite(LED2, HIGH);
             
          }
          if (msg.equals("D2"))
          {
              digitalWrite(LED2, LOW);
             
          }
        }
   }
 
}
 
void loop() {        
     client.loop();
     if(tempo_rot==500)
     {
      digitalWrite(LED_STATUS,LOW);
     }
     if(tempo_rot<1)
     {      
     tempo_rot = 1000;//reinicia contagem
     digitalWrite(LED_STATUS, HIGH);
     float Temperatura = analogRead(TEMP_LM35);    
     Temperatura = (Temperatura/1024.0) * 330;//converte para graus celsius
     Serial.print("\n\r temperatura :");     
     Serial.println(Temperatura);
     char data[6];
     char *valor = dtostrf(Temperatura,5,2,data);//float,tamanho da string,numero de casas decimais,variavel temporaria a ser usada
     client.publish("TEMP",valor);  
     if(digitalRead(BOT) == LOW)//botão pressionado?
        {
           if(est_ant_bot == 1)//botão não estava pressionado
           {
             client.publish("BOT","ON");
           }
          est_ant_bot = 0;
        }
        else
        {
          if(est_ant_bot == 0)//botão já estava pressionado
           {
             client.publish("BOT","OFF");
           }
          est_ant_bot = 1;
        }
     }
     delay(1);
     tempo_rot--;
     
}

          Quanto ao aplicativo do android esqueci de mencionar que é possível trocar os ícones ,então para este vídeo troquei eles e acrescentei mais um ícone para mostrar o status do botão.No vídeo e demonstrado o uso de um outro app do android(MQTTDASHBOAD) para plotar o gráfico da temperatura.Então vamos ao vídeo abaixo:

Vídeo de demonstração:




Até a próxima pessoal!

segunda-feira, 5 de fevereiro de 2018

NodeMcu + MQTT + ANDROID = IOT.

            Olá pessoal que acompanha o blog! nos últimos anos temos ouvido falar muito de "internet das coisas" (IOT = internet of things) , uma arquitetura pensada na interação entre dispositivos via internet e estes com o usuário. Este será o primeiro post sobre o assunto e vamos começar com algo simples, vamos colocar 2 leds para representar o acionamento de 2 lâmpadas , e mais 2 leds para representar o acionamento de 2 relés que podem estar ligados a qualquer carga.Isto será feito através de um servidor nas nuvens dedicado a IOT(geralmente chamado de "BROKER"),dentro do conceito do  BROKER existe 2 tipos de informação o publish e o subscribe  que se refere ao envio e o recebimento de dados respectivamente,veja a figura a seguir.
          Apesar do NodeMcu ser o dispositivo a ser acionado e portanto deveria apenas receber dados (subscribe) mas ele também informa quais os tópicos(tipo de dispositivo acionado) que será usado naquela conexão, alem de poder enviar status de controle, sendo por este motivo representado como uma comunicação de envio e recepção.O  protocolo usado para fazer tudo funcionar é chamado de MQTT(Message Queue Telemetry Transport) que será o responsavel por fazer as máquinas conversarem entre si M2M(Machine to Machine).
         Para fazer toda a "mágica" acontecer é preciso ter instalado o software do Arduino com as libs do ESP8266,Abra a janela de preferências na interface Arduino e digite no campo “Additional Board Manager URLs” o seguinte endereço:  http://arduino.esp8266.com/stable/package_esp8266com_index.json , depois é só instalar as bibliotecas indo na aba do gerenciador de placas do Arduino, para usar o MQTT será necessario adicionar a biblioteca "PubSubClient" que pode ser baixada(aqui) e colocada dentro da pasta "libraries" nos arquivos de instalação do Arduino, deve ficar em algo assim "C:\Arduino\arduino-1.8.5\Sketchbook\libraries\PubSubClient" eu preferi deixar a pasta do executável fora da pasta padrão do windows para não ter problemas de acesso a arquivos por parte do programa arduino. Estando com tudo corretamente preparado é só rodar o código fonte que estará anexado a este post.
        Para usar o servidor MQTT vc deve criar seu cadastro(existem vários destes procure o que achar melhor),no meu caso usei o "cloudMQTT", apenas uma obrsevação no video errei ao dizer que os servidores estavam localizados nos EUA, na verdade um fica lá(na Virginia) e o outro na Europa(Irlanda), no  vídeo de demostração vou dar mais detalhes das configurações dele.
       Para usar o Android ou qualquer outro Sistema operacional para Smartphone pode se usar uma solução pronta(existem diversas opções) para gerenciar seu dispositivo, eu usei o "MQTT Dash", no vídeo de demostração abaixo vou dar mais detalhes.
       Para melhor acompanhamento do que está acontecendo com o modulo tudo que é feito é também enviado via porta serial (emulada), ou seja através de um terminal serial RS232 podemos acompanhar todos os passos do circuito.Observe que no codigo o comando para ligar e desligar tanto os reles quanto as lâmpadas(led) é o mesmo, ou seja posso padronizar os mesmos comandos para dispositivos diversos não necessitando ter vários comandos(um pra cada dispositivo).
       O circuito elétrico  de testes da protoboard ficou assim:
        Mas você deve estar se perguntando por que usar um BROKER(servidor nas nuvens) se poderia usar uma simples pagina HTTP?A resposta é devido a 2 fatores, o primeiro é que usando um webservidor você pode acessar seu dispositivo de qualquer ponto do planeta sem a necessidade de fazer muitas configurações(caso contrario precisaria de liberar a porta de seu modem e usar um serviço de DDNS ou VPN).O segundo motivo é que usando um protocolo como o MQTT que foi pensado,preparado e padronizado para o uso de IOT a comunicação é muito mais rápido do que usar uma pagina HTTP.
O código fonte:

//Autor: Aguivone    -  microcontroladores-C
//Descricao: Placa para acionar 4 leds usado um servidor MQTT.
//Data: 18/01/18
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <ESP8266WiFi.h>
#include <PubSubClient.h> // essa lib deve ser adicionada em seu programa do arduino
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//variaveis usadas na inicialização da wifi
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const char* ssid = "nome da wifi"; //Aqui o nome da sua rede local wifi
const char* password =  "senha"; // Aqui a senha da sua rede local wifi
const char* mqttServer = "algumacoisa.cloudmqtt.com"; // Aqui o endereço do seu servidor fornecido pelo site 
const int mqttPort =16000; // Aqui mude para sua porta fornecida pelo site
const char* mqttUser = "xxxxxx"; //  Aqui o nome de usuario fornecido pelo site
const char* mqttPassword = "hfjuytsgeh"; //  Aqui sua senha fornecida pelo site

WiFiClient espClient;
PubSubClient client(espClient);
/*
Equivalencia das saidas Digitais entre nodeMCU e ESP8266 (na IDE do Arduino)
-------------------------------------------------
NodeMCU / ESP8266  |  NodeMCU / ESP8266  |
D0 = 16            |  D6 = 12            |
D1 = 5             |  D7 = 13            |
D2 = 4             |  D8 = 15            |
D3 = 0             |  D9 = 3             |
D4 = 2             |  D10 = 1            |
D5 = 14            |                     |
-------------------------------------------------
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Definindo o nome dos pinos
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const int  LED_STATUS = 2; // led de status(estou usando o led que já vem no modulo esp. 
const int  ANALOGICA = A0; // porta analogica
//////////////configura os pinos dos reles/////////////////////////////
const int RELE1 = 0; 
const int RELE2 = 4;
const int LED1 = 5;
const int LED2 = 14;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//variaveis globais
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void mqtt_callback(char* topic, byte* dados_tcp, unsigned int length);///dados_tcp = payload


void setup() { 
  pinMode(RELE1, OUTPUT);
  pinMode(RELE2, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  digitalWrite(RELE1,LOW);//desliga todos os reles
  digitalWrite(RELE2,LOW);
  digitalWrite(LED1,LOW);
  digitalWrite(LED2,LOW);
 
  Serial.begin(115200);
 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) 
  {    //inicialmente pisca o led da placa do nodemcu(é a mesma do ESP8266).
     delay(100);
    Serial.println("Conectando a WiFi..");
  }
  Serial.println("Conectado!"); 
  client.setServer(mqttServer, mqttPort);//configura servidor com as informações passadas
  client.setCallback(callback);
 
  while (!client.connected()) {
    Serial.println("Conectando ao servidor MQTT...");
    
    if (client.connect("Placa_leds", mqttUser, mqttPassword ))//colocar aqui o nome do dispositivo cliente("Placa_leds")
    {
 
      Serial.println("Conectado ao servidor MQTT!");  
      digitalWrite(LED_STATUS,HIGH); 
 
    } else {
 
      Serial.print("Falha ao conectar ");
      digitalWrite(LED_STATUS,LOW);
      Serial.print(client.state());
      delay(2000);
 
    }
  }
  
  client.publish("Status - placa_rele","Reiniciado!");
  client.publish("Placa_reles","Em funcionamento!");
  client.subscribe("RELE"); ///envia autorização para trabalhar com os topicos led e relé
  client.subscribe("LED"); 
}
 //////////////////
void callback(char* topic, byte* dados_tcp, unsigned int length) 
{
   String msg ; // cria variavel que será responsavel pelo tratamento das informações
   for(int i = 0; i < length; i++) //converte array de bytes em string
    {  char c = (char)dados_tcp[i];
       msg += c;
    } 
   digitalWrite(LED_STATUS,LOW);
   delay(100);   
   digitalWrite(LED_STATUS,HIGH); //só pra indicar que houve recepção 
   Serial.print("\n\r topico:");//sempre será o escolhido no subscribe
   Serial.print(topic);   
   Serial.print("\n\r Pacote recebido:");
   Serial.print(msg);
   if (strcmp(topic, "RELE") == 0)//recebeu comando para trabalhar com o dispositivo relé
   {
    for (int i = 0; i < length; i++) // Varre o vetor(array) inteiro do pacote recebido 
        {         
          //toma ação dependendo da string recebida:      
      
          if (msg.equals("L1"))
          {
              digitalWrite(RELE1, HIGH);
             
          }
          if (msg.equals("D1"))
          {
              digitalWrite(RELE1, LOW);
             
          }
          if (msg.equals("L2"))
          {
              digitalWrite(RELE2, HIGH);
              
          }
          if (msg.equals("D2"))
          {
              digitalWrite(RELE2, LOW);
              
          }
        }
   }
   if (strcmp(topic, "LED") == 0)//recebeu comando para trabalhar com o dispositivo led
   {
       for (int i = 0; i < length; i++) // Varre o vetor(array) inteiro do pacote recebido 
        {         
          //toma ação dependendo da string recebida:      
      
          if (msg.equals("L1"))
          {
              digitalWrite(LED1, HIGH);
              
          }
          if (msg.equals("D1"))
          {
              digitalWrite(LED1, LOW);
             
          }
          if (msg.equals("L2"))
          {
              digitalWrite(LED2, HIGH);
              
          }
          if (msg.equals("D2"))
          {
              digitalWrite(LED2, LOW);
             
          }
        }
   }
 
}
 
void loop() {        
     client.loop();
}

Vídeo de demonstração e algumas explicações:




            Espero ter ajudado a quem está iniciando neste fantástico mundo da tecnologia da internet das coisas(IOT).espero postar outros exemplos de uso em breve.