Manual
do
Maker
.
com
Imagine uma situação em que existam 2 prédios; prédio A e prédio B. Um segurança noturno passa desligando determinados pontos de energia no prédio A e posteriormente deve dirigir-se ao prédio B para o mesmo procedimento. Não seria mais eficiente se ele fosse apenas em um dos prédio e fizesse o desligamento remoto do prédio oposto? Bem, devido a um caso semelhante, tive que fazer esse pequeno projetinho - e com quase nenhum código, apenas implementando alguma funcionalidade no sketch de exemplo em Examples > RF24 > GettingStarted.
Nesse outro artigo eu mostro um modelo de painel com botoeira. No caso real, quando o botão virar para a posição de acionamento do relé, ele deve colocar o respectivo pino de um PCF8574 em GND, então o Arduino faz a leitura do estado dos pinos do PCF8574 e envia um byte (unsigned char) para o outro lado via rádio NRF24L01. O Arduino do outro lado faz a recepção dessa informação e faz a correspondência dos bits (contidos nesse byte) com a posição do array de relés, que demonstro em seguida no código implementado sobre o sketch de exemplo.
#define TURN_OFF 1 //meu modulo desliga em HIGH
#define TURN_ON 0 //e liga em LOW
// UNO
#define SIDE_A 0
#define SIDE_B 1
bool role = SIDE_B;
bool radioNumber = SIDE_B;
#define ADDRESS 0x24 //jumpers: +-- (uno)
Nessa pequena porção de código, defini ligado e desligado porque o acionamento do relé é feito com o pino de IO em LOW. Isso evita fazer confusão.
Utilizei um Arduino UNO e um Arduino Nano, como você pode ver no video. Na comunicação dos rádios, algumas configurações especificas são necessárias. O role define quem está enviando (como mestre) e quem está recebendo. Porém isso não importa de fato, porque quando um dado chega, uma resposta é enviada, então a comunicação acontece de forma bidirecional em intervalos de 1.2s (esse tempo pode ser modificado tanto para o delay do loop como para a leitura do rádio).
O rádio deve possuir um identificado. Repare no código de exemplo que há uma definição para o identificar (que não é IP nem MAC, mas apenas um array de unsigned char):
byte addresses[][6] = {"1Node","2Node"};
Nessa lista estão dispostos apenas 2 rádios, e quando definimos a variável radioNumber estamos apontando um desses dois identificadores desse array, então não cometa o erro de colocar um número maior que 1 ou menor que 0. E já que só pode ser 0 ou 1, o jeito mais simples de evitar confusão é definir a variável radioNumber com o mesmo valor de role, usando um define para SIDE_A ou SIDE_B.
Por fim, devemos definir um endereço para o PCF8574. Olhando para o módulo, você pode notar o sinal de **+**de um lado dos jumpers e **-**do lado oposto. Para o endereço 0x22 coloquei o jumper em +-- no Arduino Nano.
Defini uma variável para guardar o valor de leitura do rádio (buf) e o array de pinos dos relés. Seria muito mais simples adicionar outro PCF8574 e apenas transferir o valor advindo do rádio para ele, mas se quiser evitar wiring ou reduzir o custo em cada centavo, essa solução pode ser a mais próxima de ideal.
byte relays[8] = {2,3,4,5,6,9,10}; //pinos para cada um dos 7 reles
byte buf = 0;
E depois das poucas linhas acima, adicionei essa função para manipular os relés, o restante do sketch é o mesmo código contido no exemplo. Fácil implementar algo funcional sem esforço com Arduino, hum?
void changeRelayState(){
byte state = buf; //equipara o buffer com o comparador e faz um loop nos pinos
for (int i=0;i<7;i++){
state = buf|(1<<i); //incrementa o valor do buffer com a respectiva posição
//se for possível incrementar o valor do buffer, significa que o
//pino do PCF8574 está em LOW
if (state != buf){
digitalWrite(relays[i],TURN_OFF); //nesse caso, desliga o relay da posição 'i'
Serial.print("OFF: ");
Serial.println(relays[i]);
state = buf;
}
/*Senão, liga o relay da posição 'i'*/
else{
digitalWrite(relays[i],TURN_ON);
Serial.print("ON: ");
Serial.println(relays[i]);
}
}
}
O código completo ficou assim (usando como base o Arduino UNO):
/*
* Getting Started example sketch for nRF24L01+ radios
* This is a very basic example of how to send data from one node to another
* Updated: Dec 2014 by TMRh20
*/
#include <SPI.h>
#include "RF24.h"
#include <Wire.h>
#define TURN_OFF 1 //meu modulo desliga em HIGH
#define TURN_ON 0 //e liga em LOW
// UNO
#define SIDE_A 0
#define SIDE_B 1
bool role = SIDE_B;
bool radioNumber = SIDE_B;
#define ADDRESS 0x24 //jumpers: +-- (uno)
/****************** User Config ***************************/
/*** Set this radio as radio number 0 or 1 ***/
/* Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 7 & 8 */
RF24 radio(7,8);
/**********************************************************/
byte addresses[][6] = {"1Node","2Node"};
byte relays[8] = {2,3,4,5,6,9,10}; //pinos para cada um dos 7 reles
byte buf = 0;
void changeRelayState(){
byte state = buf; //equipara o buffer com o comparador e faz um loop nos pinos
for (int i=0;i<7;i++){
state = buf|(1<<i); //incrementa o valor do buffer com a respectiva posição
//se for possível incrementar o valor do buffer, significa que o
//pino do PCF8574 está em LOW
if (state != buf){
digitalWrite(relays[i],TURN_OFF); //nesse caso, desliga o relay da posição 'i'
Serial.print("OFF: ");
Serial.println(relays[i]);
state = buf;
}
/*Senão, liga o relay da posição 'i'*/
else{
digitalWrite(relays[i],TURN_ON);
Serial.print("ON: ");
Serial.println(relays[i]);
}
}
}
void setup() {
Wire.begin();
Serial.begin(115200);
for (byte i=0;i<2;i++){
pinMode(relays[i],OUTPUT);
}
Serial.println(F("RF24/examples/GettingStarted"));
Serial.println(F("*** PRESS 'T' to begin transmitting to the other node"));
radio.begin();
// Set the PA Level low to prevent power supply related issues since this is a
// getting_started sketch, and the likelihood of close proximity of the devices. RF24_PA_MAX is default.
radio.setPALevel(RF24_PA_LOW);
// Open a writing and reading pipe on each radio, with opposite addresses
if(radioNumber){
radio.openWritingPipe(addresses[1]);
radio.openReadingPipe(1,addresses[0]);
}else{
radio.openWritingPipe(addresses[0]);
radio.openReadingPipe(1,addresses[1]);
}
// Start the radio listening for data
radio.startListening();
}
void loop() {
/****************** Ping Out Role ***************************/
if (role == 1) {
byte state;
radio.stopListening(); // First, stop listening so we can talk.
Serial.println(F("Now sending"));
Wire.beginTransmission(ADDRESS); //inicia transmissao no barramento i2c
Wire.write(0xFF); //coloca tudo em HIGH. O que estiver em GND vai para LOW sozinho.
Wire.endTransmission(); //fim do envio do comando.
delay(1000);
Wire.requestFrom(ADDRESS,1); //solicita 1 byte (8 bits)
if (Wire.available()){
state = Wire.read();
}
Serial.print("Enviando: ");
Serial.println(state);
if (!radio.write(state, sizeof(byte) )){
Serial.println(F("failed"));
}
radio.startListening(); // Now, continue listening
unsigned long started_waiting_at = micros(); // Set up a timeout period, get the current microseconds
boolean timeout = false; // Set up a variable to indicate if a response was received or not
while ( ! radio.available() ){ // While nothing is received
if (micros() - started_waiting_at > 200000 ){ // If waited longer than 200ms, indicate timeout and exit while loop
timeout = true;
break;
}
}
if ( timeout ){ // Describe the results
Serial.println(F("Failed, response timed out."));
}else{
byte got_time; // Grab the response, compare, and send to debugging spew
radio.read( &got_time, sizeof(byte) );
unsigned long end_time = micros();
buf = got_time;
changeRelayState();
// Spew it
Serial.print(F("Sent "));
Serial.print(F(", Got response "));
Serial.print(got_time);
Serial.print(F(", Round-trip delay "));
Serial.println(F(" microseconds"));
}
// Try again 1s later
delay(1000);
}
/****************** Pong Back Role ***************************/
if ( role == 0 )
{
byte got_time;
if( radio.available()){
// Variable for the received timestamp
while (radio.available()) { // While there is data ready
radio.read( &got_time, sizeof(byte) ); // Get the payload
buf = got_time;
changeRelayState();
}
Wire.beginTransmission(ADDRESS); //inicia transmissao no barramento i2c
Wire.write(0xFF); //coloca tudo em HIGH. O que estiver em GND vai para LOW sozinho.
Wire.endTransmission(); //fim do envio do comando.
delay(1000);
Wire.requestFrom(ADDRESS,1); //solicita 1 byte (8 bits)
if (Wire.available()){
got_time = Wire.read();
}
Serial.print("Enviando: ");
Serial.println(got_time);
radio.stopListening(); // First, stop listening so we can talk
radio.write( &got_time, sizeof(byte) ); // Send the final one back.
radio.startListening(); // Now, resume listening so we catch the next packets.
Serial.print(F("Sent response "));
Serial.println(got_time);
}
}
/****************** Change Roles via Serial Commands ***************************/
if ( Serial.available() )
{
char c = toupper(Serial.read());
if ( c == 'T' && role == 0 ){
Serial.println(F("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK"));
role = 1; // Become the primary transmitter (ping out)
}else
if ( c == 'R' && role == 1 ){
Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK"));
role = 0; // Become the primary receiver (pong back)
radio.startListening();
}
}
} // Loop
O resultado você vê no video a seguir.
Inscreva-se no nosso canal Manual do Maker no YouTube.
Também estamos no Instagram.
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.