Executar comandos remotos com RF 433MHz - Automação Residencial

Boa noite senhores, recentemente criei um post com meu código para automação residencial.
Agora é o seguinte, só falta uma penúltima etapa para terminar o código, que é separar as funções de acender/apagar lâmpadas, alarmes, sensores, em vários módulos slaves, de acordo com os cômodos da casa.
Reparem então no código principal, que tem todas as funções no mesmo arduino

#include <SPI.h>
#include <String.h>
#include <Ethernet.h>

byte mac[] = { 0x01,0x23,0x45,0x67,0x89,0xAB }; // Endereço Mac
byte ip[] = { 192, 168, 0, 50 }; // Endereço de Ip da sua Rede
EthernetServer server(80); // Porta de serviço

String readString = String(30); // string para buscar dados de endereço
boolean statusFiltro = false; // Variável para o status do led
boolean statusAlarme = false; // Status do meu alarme

/*
##########################
------Boolean Luzes-------
##########################
*/
boolean SL1C = false;
/*
##########################
------Boolean Luzes-------
##########################
*/
float temperatura = 0;
float ldr = 0;

int pin2 = 13;


//Definição dos componentes do alarme
int pinBuzzer = 7; 
int pinSensorPIR = 8;
int pinLed = 9;
int valorSensorPIR = 0;

void setup(){
  // Inicia o Ethernet
  Ethernet.begin(mac, ip);
  // Define o pino como saída
  // Inicia a comunicação Serial
  Serial.begin(9600); 
  
  pinMode(pinBuzzer, OUTPUT);
  pinMode(pinSensorPIR, INPUT);
  pinMode(pinLed, OUTPUT);
}

void loop(){
  // Criar uma conexão de cliente
  valorSensorPIR = digitalRead(pinSensorPIR); // Configuração do sensor
  Serial.print("Valor do Sensor PIR: ");  
  Serial.println(valorSensorPIR);
  delay(300);
  
  EthernetClient client = server.available();
  int valorLido = analogRead(0);
  temperatura = (valorLido/31);
  
  int valorLdr = analogRead(1);
  ldr = (valorLdr);
  
  if (client) {
    while (client.connected())
    {
      if (client.available())
      {
        char c = client.read();
        // ler caractere por caractere vindo do HTTP
        if (readString.length() < 30)
        {
          // armazena os caracteres para string
          readString += (c);
        }
        
        //se o pedido HTTP terminou
        if (c == '\n')
          {
             Serial.print("Mensagem recebida: ");
             Serial.println(readString);
         if(readString.indexOf("lAlarme")>=0){ // Se o comando lAlarme for recebido ele muda o status e faz a função ligarAlarme
             Serial.println("Alarme ACTIVADO.");
            statusAlarme = true;    
         }
          if(readString.indexOf("dAlarme")>=0){ // Se receber o comando dAlarme, apenas muda o status e desliga o alarme
             Serial.println("Alarme DESACTIVADO.");
            statusAlarme = false;
            desligarAlarme();
          }
          if(readString.indexOf("lFiltro")>=0){
            statusFiltro = true;
          }
          if(readString.indexOf("dFiltro")>=0){
            statusFiltro = false;
          }
          if(readString.indexOf("L1C")>=0){ //COMANDO QUE QUERO USAR NO SLAVE
            if(SL1C){
              digitalWrite(pin2, LOW);
              SL1C = false;
            }else{
              digitalWrite(pin2, HIGH);
              SL1C = true;
            }
          }
        // dados HTML de saída começando com cabeçalho padrão
        client.println("HTTP/1.1 200 OK");
        client.println("Content-Type: text/html");
        client.println();
        
        client.print("<font size='20'>");
       // separa as linhas
        
        if(statusAlarme){
        client.print("AlarmeOn");
        }else{
          client.print("AlarmeOff");
        }
       
        client.print("<P>");
        
        if(statusFiltro){
        client.print("FiltroOn");
        }else{
          client.print("FiltroOff");
        }
       
        client.print("<P>");
        
        client.print("Temperatura: ");
        client.print(temperatura);
        client.print(" graus Celsius");
        
        client.print("<P>");
        
        client.print("LDR: ");
        client.print(ldr);
        client.print(" lux");
        
        client.print("<P>");
        
        if(SL1C){
        client.print("LuzOn");
        }else{
          client.print("LuzOff");
        }
       
       client.print("<P>");
       
       if(valorSensorPIR == 1){
         client.print("MovOn");
       }else{
         client.print("MovOff");
       }
          
        
        //limpa string para a próxima leitura
        readString="";
        
        // parar cliente
        client.stop();
        }
      }
    }
  }
  if (valorSensorPIR == 1 && statusAlarme == 1) {
             ligarAlarme();
  }
  
}

void ligarAlarme() {
  digitalWrite(pinLed, HIGH);
  tone(pinBuzzer,1500);
} 
void desligarAlarme() {
  //Desligando o led
  digitalWrite(pinLed, LOW);
  //Desligando o buzzer
  noTone(pinBuzzer);
}

Aí tem algumas funções. Queria fazer o seguinte. Ao receber o comando "L1C", quero que ele ligue o led que será ligado em outro arduino, o slave.

Porém não faço a menor ideia de quais alterações devo fazer no programa principal, e o que deve ter no programa secundário.
O modelo de RF 433MHz que tenho é esse:

Já tenho a biblioteca VirtualWire.h instalada, mas não está inclusa no programa.

Li vários tópicos relacionados, mas não consegui entender. Se puderem me dar a base de um led, acho que posso fazer para o restante.

Qualquer informação que esteja faltando, me avise que informarei.
Desde já agradeço.

O que tu estás à procura é dum protocolo de comunicação.

Ou seja, um arduino envia algo para o outro e o outro executa uma acção. Um protocolo pode ser tão simples como

Serial.print("A");

Ou bastante mais complexo com métodos para detectar e até corrigir erros de comunicação. A questão que se põe é, pretendes apenas ligar um LED num (único) arduino, ou pretendes que isto seja mais abrangente com vários Arduinos a comunicar com um central?

A comunicação é apenas num sentido ou em dois sentidos?

Portanto, existe comunicação em dois sentidos... o master activa o alarme e o slave envia o estado do alarme se algo acontecer.

Como vais saber que a comunicação está a funcionar se não tens um feedback?

Se tencionas ter mais que um slave, então tens de ter algo que distinga um slave do outro. Assim sendo, vais precisar no minimo de enviar dois caracteres. Um para te indicar o comando... outro para te indicar qual o nó que o deve executar.

Como vais fazer para garantir que os nós não vão interferir um com o outro?

O problema é que no que toca a comunicações não existe grande e pequena escala. A partir do momento que tens mais que um nó, o trabalho é o mesmo que teres n... pois já tens de garantir que não existe interferência e que o protocolo é bom o suficiente para o que pretendes.

Como vais transmitir com um RF desse género, será aconselhável também teres um método de detectares erros... como um checksum, por exemplo.

Existe um post sobre automação residencial no fórum onde este assunto de comunicações foi falado e examinado em grande detalhe... são 30 e tal páginas, se não me engano, mas com muito sumo... :slight_smile:

Uma pesquisa no fórum e não é difícil de encontrar...

http://forum.arduino.cc/index.php?topic=149801.0

Lá mais para a página 20 ou isso, a pessoa que fez o post original abandonou a ideia de usar um par de cobre para uma rede RS485 e estava a ver como fazer isto com rádios. Daí a relevância desse post para ti. No entanto, a possibilidade do rádio foi discutida várias vezes antes disso.

Como vais ligar o Arduino?
Ele vai ficar à vista? Uma fonte de alimentação para o Arduino é outro dos problemas focados nessa thread de 30 páginas... E a solução não é muito amigável.
Creio que o rádio também sofre se estiver embutido numa parede.

O meu projecto de sensores está a avançar muito pouco. Principalmente porque tenho de soldar e detesto fazer isso em veroboard. No entanto o rádio pareceu funcionar aceitavelmente bem e para o próximo mês conto ter o sistema a funcionar para testes e depurar o código.

Entretanto comprei um disco de rede... e qual não é o meu espanto ao ver que aquilo traz um servidor SQL e corre PHP. :slight_smile: Já tenho onde armazenar os dados.

Como rádio, estou a usar o nRF24L01... que se não me engano foi a solução mais razoável. O XBee é sem dúvida a mais fácil de utilizar.

Antes de prosseguires com isto, a tua mulher sabe o que pretendes fazer e como vai ficar à vista? Eu digo isto porque se dissesse à minha que ia ter de abrir uma parece ou deixar um carregador de telemóvel de fora da parede para poder ligar uma lâmpada com o telemóvel ela provavelmente pediria o divórcio.

Hoje em dia um carregador de telemóvel USB faz o que pretendes por um preço aceitável... desde que não seja dos chineses.

O nRF24L01 é bom... mas é complicado de trabalhar devido a não ser comunicação de duas vias. Por outro lado, os resultados dentro de casa também não são grande espingarda, eu por exemplo, no pequeníssimo teste que fiz não notei problemas.

O pro mini de 3,3V é bastante interessante e para usar os nRF ou XBee ideais devido à alimentação de 3,3V. Eu decidi usar os nano por ser preguiçoso e não pretender fazer nada de muito ambicioso... para já.

Módulo turnkey 433MHz fácil compatível com arduino:
http://www.appconwireless.com/PRODUCTS/showproduct.php?lang=en&id=7

Mas então, o que devo fazer pra começar a utilizar este módulo? Devo impor quais libs? Quais códigos devo incrementar para fazer a comunicação? O que deve ter no Master e o que deve ter nos slaves.. São coisas mais específicas que procuro. Tenho grandes dificuldades em interpretar códigos, por isso peço ajuda, mas sei que não é desculpa para ficar parado e esperando, e inclusive estou lendo alguns exemplos para realizar esta parte do projeto.

Obrigado e conto com vocês.

Tirei o ponteiro, e agora ele entra no if
Porém, só me retorna isto:

Got: 
68656C6C6Frecebido
Got: 
68656C6C6F32recebido

tanto no lFiltro como no dFiltro, ou seja, apenas acende o led. No segundo if, mudei para

Serial.print("recebido2");

mas não me retornou isto.

Se jogares com o ponteiro que esta comentado veras que o strcmp() funciona como queres

#include <VirtualWire.h>


void setup()
{

Serial.begin(9600);
Serial.println("setup");

}
void loop()
{
//char *rawData = "Vou entrar no primeiro if";
char *rawData = "Vou entrar no segundo if";
if (strcmp(rawData, "Vou entrar no primeiro if")  == 0 )  //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("primeiro if");
    }  
if (strcmp(rawData, "Vou entrar no segundo if")  == 0) //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("segundo if");
    } 
}

Agora é so juntares o teu codigo ...

Meu code ficou assim:

#include <VirtualWire.h>
int led = 7;

void setup()
{
pinMode(led, OUTPUT);
Serial.begin(9600);
Serial.println("setup");
vw_setup(2000); // Bits per sec
vw_rx_start(); // Start the receiver PLL running
}
void loop()
{
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
if(vw_get_message(buf, &buflen)) // Non-blocking
{
int i;
// Message with a good checksum received, dump HEX
Serial.println("Got: ");
for (i = 0; i < buflen; i++)
{
Serial.print(buf[i], HEX);
Serial.println("");
}
//char *rawData = "Vou entrar no primeiro if";
char *rawData = "Vou entrar no segundo if";
if (strcmp(rawData, "Vou entrar no primeiro if")  == 0 )  //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("primeiro if");
    }  
if (strcmp(rawData, "Vou entrar no segundo if")  == 0) //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("segundo if");
    } 
}
}

Mas não funcionou.

Tentei deixar assim:

//char *rawData = "hello";
char *rawData = "hello2";
if (strcmp(rawData, "hello")  == 0 )  //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("primeiro if");
    }  
if (strcmp(rawData, "hello2")  == 0) //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("segundo if");
    } 
}

E só o que me retorna quando envio hello (lFiltro) é:

Got: 
68656C6C6F
segundo if

o mesmo ocorre quando envio hello2 (dFiltro), porém o que muda é o code:

68656C6C6F32

Eu sinceramente nao sei o que queres...
Ja experimentaste, talvez... nao imprimir o que recebes em HEX??

char buf[10]; // importante ser char... 

Serial.print(buf[i]); //nada de HEX... 
Serial.println("");

Mete tambem o codigo de envio porque deve haver algo estranho la.

Nota que tu estas a assumir que a mensagem chega toda ao mesmo tempo... isso nao e verdade.

Aqui está o código simplificado de envio (master), contendo apenas a lib, setup necessário do componente, e no loop, o comando que envia para o slave:

#include <VirtualWire.h>

void setup(){
  vw_setup(2000); // Bits per sec
  // Inicia a comunicação Serial
  Serial.begin(9600);
}

void loop(){
if(readString.indexOf("lFiltro")>=0){
            statusFiltro = true;
            const char *msg = "hello";
            vw_send((uint8_t *)msg, strlen(msg));
            delay(400);
          }
          if(readString.indexOf("dFiltro")>=0){
            statusFiltro = false;
            const char *msg = "hello2";
            vw_send((uint8_t *)msg, strlen(msg));
            delay(400);
          }
....
}

Sem HEX, obtive

Got: 
104
101
108
108
111

Ele entrou no segundo if, porém, não consigo entrar no primerio, pois o *rawData referente ao primeiro if está comentado, e se eu tirar o "//" ele acusa "Redeclaration"

E mudaste para char?

Esses numeros sao o teu hello... ve aqui.... http://www.asciitable.com/

Tu so podes definir uma variavel uma vez... quando tiras o comentario da linha, tens de comentar a outra. Nao podes ter a variavel rawData declarada duas vezes.

Hum.. Identifiquei o hello pelo site, mas não mudei para char, deixei no for

for (i = 0; i < buflen; i++)
{
Serial.print(buf[i]);
Serial.println("");
}

quanto as variáveis, como vou alternar entre primeiro if e segundo if se só posso usar uma de cada vez?

//char *rawData = "Vou entrar no primeiro if";
char *rawData = "Vou entrar no primeiro if";
if (strcmp(rawData, "Vou entrar no primeiro if")  == 0 )  //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("primeiro if");
    }  
if (strcmp(rawData, "Vou entrar no segundo if")  == 0) //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("segundo if");
    } 
}

yago4xd:
quanto as variáveis, como vou alternar entre primeiro if e segundo if se só posso usar uma de cada vez?

//char *rawData = "Vou entrar no primeiro if";

char *rawData = "Vou entrar no primeiro if";
if (strcmp(rawData, "Vou entrar no primeiro if")  == 0 )  //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
    Serial.println("primeiro if");
    } 
if (strcmp(rawData, "Vou entrar no segundo if")  == 0) //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
    Serial.println("segundo if");
    }
}

Amigo, ele te deu um exemplo de uso, não uma solução pronta...
mude rawdata para "buf" e os IFs vão analisar a mensagem recebida pelo RF

if (strcmp(buf, "hello2")  == 0 )  //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     //liga saida!
    }

Se entendi o que me alertaram, o código seria isso:

#include <VirtualWire.h>
int led = 7;

void setup()
{
pinMode(led, OUTPUT);
Serial.begin(9600);
Serial.println("setup");
vw_setup(2000); // Bits per sec
vw_rx_start(); // Start the receiver PLL running
}
void loop()
{
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
if(vw_get_message(buf, &buflen)) // Non-blocking
{
int i;
// Message with a good checksum received, dump HEX
Serial.println("Got: ");
for (i = 0; i < buflen; i++)
{
Serial.print(buf[i]);
Serial.println("");
}
char buf[10];
//char *rawData = "Vou entrar no segundo if";
if (strcmp(buf, "hello")  == 0 )  //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("primeiro if");
    }  
if (strcmp(buf, "hello2")  == 0) //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("segundo if");
    } 
}
}

Não posso testar agora pois estou fora de casa.

PS: Editei o post pois as alterações do código não foram salvas, agora está o código que penso ter entendido.

Não obtive sucesso, tentei usar um ponteiro no char buf

void loop()
{
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
if(vw_get_message(buf, &buflen)) // Non-blocking
{
int i;
// Message with a good checksum received, dump HEX
Serial.println("Got: ");
for (i = 0; i < buflen; i++)
{
Serial.print(buf[i]);
Serial.println("");
}

char* buf;

if (strcmp(buf, "hello")  == 0 )  //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("primeiro if");
    }  
if (strcmp(buf, "hello2")  == 0) //strcmp compara a string com o valor caso seja verdadeiro devolve valor 0
    {
     Serial.println("segundo if");
    } 
}
}

No serial monitor ele identifica corretamente o código hello (104 101 108 108) e hello2 (104 101 108 108 50)
porém nos ifs, ele não identifica que o buf, "hello" ou buf, "hello2" representa estes valores.

Onde estou errando? Creio que não estou recolhendo corretamente as suas informações e adaptá-las ao meu código.

Em vez de andarmos aqui a fazer o programa por ti, nao achas melhor estudares um pouco de C? Neste momento estas pertissimo e falhas por nao teres as minimas bases.

Esse programa compila??? Eu disse mais atras que nao podes redefinir a mesma variavel em dois sitios... e tu sais-te com isto:

void loop()
{
uint8_t buf[VW_MAX_MESSAGE_LEN];
....
char* buf;

O que e que nao e obvio para ti que a variavel buf vai receber o que o outro dispositivo te envia e como tal e essa variavel que tem de ser usada na comparacao.

Esta linha tem de sair:

char* buf;

Hahahaha
Realmente precisei tomar vergonha na cara. Não estudei C propriamente dito. Estudei mais sobre o RF 433MHz, e posso dizer que conclui minha dúvida. Não utilizei strcmp, nem const char, apenas char.

Observe as mudanças no master e slave:

Master

void setup(){
    vw_setup(2000);	 // Bits per sec
}

void loop(){
  char *msg;

 if(readString.indexOf("lFiltro")>=0){
            statusFiltro = true;
            char *msg = "1"; //Resolvi trocar para simplificar
            vw_send((uint8_t *)msg, strlen(msg));
            vw_wait_tx(); // Wait until the whole message is gone
          }
          if(readString.indexOf("dFiltro")>=0){
            statusFiltro = false;
            char *msg = "2";  //Resolvi trocar para simplificar
            vw_send((uint8_t *)msg, strlen(msg));
            vw_wait_tx(); // Wait until the whole message is gone
          }

Slave

 #include <VirtualWire.h>
 
int led = 7;

void setup()
{
    Serial.begin(9600);	// Debugging only
    Serial.println("setup");

    // Initialise the IO and ISR
    vw_setup(2000);	 // Bits per sec
    vw_rx_start();       // Start the receiver PLL running

pinMode(led, OUTPUT);
}

void loop()
{
  
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;

    if (vw_get_message(buf, &buflen)) // Non-blocking
    {
	int i;

        //digitalWrite(13, true); // Flash a light to show received good message
	// Message with a good checksum received, dump it.
	Serial.print("Got: ");
	
	for (i = 0; i < buflen; i++)
	{
	    Serial.print(buf[i]);
        if(buf[i] == '1'){digitalWrite(led, HIGH);}
        if(buf[i] == '2'){digitalWrite(led, LOW);}
	    Serial.print(" ");
	}
	Serial.println("");

   }
}

Agora o slave recebe 49 e 50, que de acordo com a tabela que me passaste, é realmente 1 e 2 (Não usei mais o hello, para simplificação)

O led atuou corretamente, portanto, minha comunicação está quase pronta. Basta agora, fazer aquilo que tinha falado no início, que era para o slave enviar ao master, se houve ou não, movimento através de sensor PIR.

Penso em fazer do mesmo jeito, usando o code do master no slave e o do slave no master, mas para isso, no master terá que ter um receptor especial para o alarme, e o slave responsável pelo alarme, terá que ter um transmissor específico também. Minha dúvida ainda é se ocorrerá conflitos, já que no mesmo código, haverá transmissor e receptor.

Outra solução, é trabalhar com o próprio receptor do slave em questão, usando-o como resposta. Ainda não sei como vou fazer para retornar os dados quando o sensor = 1. Quem souber de fontes que ensinam sobre resposta do rf 433mhz agradeço. Vou pesquisar sobre também.

Mas entre usar o método resposta ou colocar outro transmissor e receptor, qual seria melhor?

Obrigado!