En esta sección aprenderás a establecer una comunicación inalámbrica en modo simplex utilizando dos módulos Bluetooth para enviar y recibir datos. El objetivo es establecer la comunicación inalámbrica entre un PIC Maestro y un PIC Esclavo para enviar el valor de un potenciometro para mostrarlo en una pantalla OLED.
Bluetooth HC-05
El Bluetooth HC-05 es un módulo de comunicación inalámbrico que utiliza la tecnología Bluetooth para establecer un enlace de comunicación serie entre dos dispositivos. Es un dispositivo muy popular en proyectos de electrónica y robótica, ya que permite la comunicación inalámbrica entre un microcontrolador, como Arduino, y otros dispositivos como un ordenador, smartphone o tablet. El HC-05 puede funcionar como maestro o esclavo y utiliza el perfil serial port profile (SPP) de Bluetooth, lo que lo hace compatible con una amplia variedad de dispositivos que soportan este perfil.
Características:
- Tecnología: Bluetooth 2.0 + EDR (Enhanced Data Rate)
- Alcance de comunicación: hasta 10 metros (en condiciones ideales)
- Velocidad de transmisión: 2.1 Mbps máx. en modo EDR, 721 kbps máx. en modo estándar
- Frecuencia: 2.4 GHz a 2.4835 GHz banda ISM
- Potencia de transmisión: Clase 2, hasta 4 dBm (2.5 mW)
- Sensibilidad de recepción: -80 dBm típico
- Protocolos de soporte: Bluetooth serial port profile (SPP)
- Modo de operación: Maestro o Esclavo
- Compatibilidad: Compatible con dispositivos que soportan Bluetooth SPP
- Interfaz: UART (Universal Asynchronous Receiver/Transmitter)
- Voltaje de alimentación: 3.3V a 5V DC
- Consumo de energía: 30 mA en modo de operación, 1 mA en modo de espera
- Dimensiones: 28 mm x 15 mm x 2.35 mm
Circuito de conexión
Configuración
Inicialmente se configuran los módulos Bluetooth en modo maestro y esclavo para transmitir los datos mediante una comunicación inalámbrica.
Programación PIC Maestro
Inicialmente se establecen los parámetros de la comunicación RS232.
- baud = Velocidad de transmisión y recepción configurada en el módulo Bluetooth.
- stop = Bits de parada.
- parity = Bit de paridad.
- xmit = Pin de transmisión.
- rcv = Pin de recepción.
- bits = Datos de transmisión.
- stream = identificación de transmisión.
#use rs232(baud=38400, stop=1, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)
Se habilita la conversión analógica a digital.
setup_adc(ADC_CLOCK_INTERNAL);
Se habilita la lectura en el pin AN0, se hace la lectura del valor y se guarda en la variable «POT1».
set_adc_channel (0);//Lectura Vout del potenciometro en PIN AN0
delay_us(10);
POT1 = read_adc();// lectura del valor
Se utiliza una sentencia «if» para enviar el valor del pin AN0 cada vez que el valor cambie, con esto se evita enviar el mismo valor múltiples veces.
if (pasPOT1 != POT1)
{
printf("POT1%lu\r",POT1);
pasPOT1 = POT1;
}
La función «printf» envía inicialmente un identificador para indicarle al PIC esclavo que el valor corresponde al «POT2» después se coloca el valor entero de 16 bits y finalmente se utiliza «/r» para indicar el final de los datos enviados.
printf("POT2%lu\r",POT2);
Código completo PIC Maestro
#FUSES NOMCLR
#use rs232(baud=38400, stop=1, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)
void main()
{
setup_adc(ADC_CLOCK_INTERNAL);
int16 POT1=0; //variable para guardar el valor actual
int16 POT2=0;
int16 pasPOT1=0; //variable para guardar el valor pasado
int16 pasPOT2=0;
while(TRUE)
{
set_adc_channel (0);//Lectura Vout del potenciometro en PIN AN0
delay_us(10);
POT1 = read_adc();// lectura del valor
if (pasPOT1 != POT1)
{
printf("POT1%lu\r",POT1);
pasPOT1 = POT1;
}
delay_ms(100);
set_adc_channel (1);//lectura Vout del potenciometro en PIN AN1
delay_us(10);
POT2 = read_adc();// lectura del valor
if (pasPOT2 != POT2)
{
printf("POT2%lu\r",POT2);
pasPOT2 = POT2;
}
delay_ms(100);
}
}
Programación PIC Esclavo
Inicialmente se establecen los parámetros de la comunicación RS232.
- baud = Velocidad de transmisión y recepción configurada en el módulo Bluetooth.
- stop = Bits de parada.
- parity = Bit de paridad.
- xmit = Pin de transmisión.
- rcv = Pin de recepción.
- bits = Datos de transmisión.
- stream = identificación de transmisión.
#use rs232(baud=38400, stop=1, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)
Se habilitan las interrupciones para la recepción de datos por el puerto RS232.
enable_interrupts(int_rda);
enable_interrupts(global);
Cuando se reciben los datos por el puerto RS232, la interrupción se activa y el programa se aloja en la sub-rutina «rda_isr()» donde se reciben los datos enviados por el PIC maestro y se guardan en la variable «recibido[]».
#int_rda
void rda_isr()
{
memset(recibido, 0, sizeof(recibido)); //limpia los registros de la varible
int8 i=0;
while(true)
{
recibido[i]=getc();//RECIBE EL CARACTER
if(recibido[i]=='\r')//busca el salto de linea \r
{
recibido[i]='\0';//fin de la cadena = caracter nulo \0
datos();
mostrar();
return;
}
i++;
}
}
Cuando se reciben todos los datos, en la sub-rutina «datos()» se procesan para identificar el valor del potenciometro que se esta recibiendo.
void datos()
{
char POT1[10] = "POT1";
char POT2[10] = "POT2";
.
.
.
Inicialmente se identifica el potenciometro del cual se reciben los datos y se guarda el valor.
Los primeros cuatro caracteres de la variable «recibido[]», corresponden al potenciometro y los cuatro caracteres siguientes corresponden a el valor del potenciometro, por lo que se guardan el variables separadas.
for (int i=0; i<=3; ++i)
{
ID_POT[i] = recibido[i]; //identifica el potennciometro
}
for (int i=0; i<=3; ++i)
{
num[i] = recibido[i+4]; //guarda el valor del potennciometro
}
Se realiza una comparación para identificar el potenciometro.
La función «strcmp()» compara dos cadenas de caracteres, en este caso si es igual entonces el resultado es cero «0».
En la sentencia «if» se utiliza la función «atol()» que convierte una cadena de caracteres en un numero de 16 bits. En este caso se convierte la cadena de caracteres de la variable «num» en un valor de 16 bits y se guarda en la variable correspondiente.
result = strcmp(ID_POT, POT1);//compara, si son iguales (0), no (+-1)
if(result==0)
{
val_POT1 = (int16)atol(num); //convierte la cadena en un numero de 16bits
return;
}
Finalmente se muestra el valor recibido de cada potenciometro en el display OLED.
void mostrar()
{
char texto[20];
sprintf(texto,"POT1=%04lu",val_POT1);
OLED_DrawText(1,1,texto,1);
sprintf(texto,"POT2=%04lu",val_POT2);
OLED_DrawText(1,10,texto,1);
OLED_Display();//Muestra la información en pantalla;
}
Código completo PIC Esclavo
#include "stdlib.h"
#FUSES NOMCLR
#use i2c(Master, Fast, sda=PIN_B0, scl=PIN_B1,force_sw, stream=OLED_stream)//parametros I2C
#use rs232(baud=38400, stop=1, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)
#define SH1106_128_64 //DEFINE EL MODELO DE LA PANTALLA OLED
//#define SSD1306_128_64
#include "OLED_I2C.c" //libreria oled I2C
char recibido[20];
int16 val_POT1=0;
int16 val_POT2=0;
void datos();
void mostrar();
#int_rda
void rda_isr()
{
memset(recibido, 0, sizeof(recibido)); //limpia los registros de la varible
int8 i=0;
while(true)
{
recibido[i]=getc();//RECIBE EL CARACTER
if(recibido[i]=='\r')//busca el salto de linea \r
{
recibido[i]='\0';//fin de la cadena = caracter nulo \0
datos();
mostrar();
return;
}
i++;
}
}
void datos()
{
signed int8 result;
char ID_POT[10];
char num[10];
char POT1[10] = "POT1";
char POT2[10] = "POT2";
memset(ID_POT, 0, sizeof(ID_POT)); //limpia los registros de la varible ID_POT
memset(num, 0, sizeof(num)); //limpia los registros de la varible num
for (int i=0; i<=3; ++i)
{
ID_POT[i] = recibido[i]; //identifica el potennciometro
}
for (int i=0; i<=3; ++i)
{
num[i] = recibido[i+4]; //guarda el valor del potennciometro
}
result = strcmp(ID_POT, POT1);//compara, si son iguales (0), no (+-1)
if(result==0)
{
val_POT1 = (int16)atol(num); //convierte la cadena en un numero de 16bits
return;
}
result = strcmp(ID_POT, POT2);
if(result==0)
{
val_POT2 = (int16)atol(num); //convierte la cadena en un numero de 16bits
return;
}
}
void mostrar()
{
char texto[20];
sprintf(texto,"POT1=%04lu",val_POT1);
OLED_DrawText(1,1,texto,1);
sprintf(texto,"POT2=%04lu",val_POT2);
OLED_DrawText(1,10,texto,1);
OLED_Display();//Muestra la información en pantalla;
}
void main()
{
enable_interrupts(int_rda);
enable_interrupts(global);
OLED_Begin();
OLED_ClearDisplay();
mostrar();
while(TRUE)
{
//TODO: User Code
}
}