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

Olá senhores, volto aqui para atualizar a minha situação quanto a comunicação RF. Optei por fazer uma comunicação dupla, com comando e resposta. Para isso, apenas aumentei o número de componentes, sendo um transmissor e um receptor por arduino, e não só transmissor no master e receptor no arduino.

Com isso, posso fazer algo do tipo:

if(buf[i] == '1'){
        digitalWrite(led, HIGH);
          //const char *msg = "A";
          //vw_send((uint8_t *)msg, strlen(msg));
          //vw_wait_tx();
          Serial.println("Entrou 1 e Saiu A");
      }

Caso receba 1 do master, ele faz a função determinada e envia uma resposta ("A") para o master.

if(buf[i] == 'A'){
         statusFiltro = true;
         Serial.println("Recebeu A");
         }

E no master, se ele de fato recebeu a resposta do slave, aí sim ele pode mudar o status, confirmando na página html que houve de fato, uma mudança no estado da lâmpada por exemplo.

Obviamente, há por trás destes exemplos, todo o código necessário para envio e resposta dos RFs

Um problema que tive agora, é:

Como posso enviar valores analógicos obtidos de um slave para o master? Na minha aplicação, terá um slave na cozinha, e nele, vários sensores (chamas, umidade, temperatura e gás).
Seria possível, enviar o valor da temperatura por exemplo para o master via RF 433MHz? Pois quero todos esses valores na página HTML.

Aguardo ajuda. Obrigado!

Claro que é... O que procuras é algo chamado de protocolo.

Procura no fórum por isso já que é das dúvidas mais frequentes ou dá uma olhada aqui

http://microsrus.blogspot.co.uk

Fui eu que escrevi e está incompleto, mas deve dar uma ajuda a entender o que é um protocolo.

Vish.. Encontrei muitas respostas, todas elas voltadas para um protocolo mais robusto. Ainda é muita informação, ainda mais que acabou de explodir minha cafeteira na cozinha... :confused:

Quando eu descobri o que foi, vou pensar em usar o arduino para evitar problemas do tipo kkkkkkk

Enfim, ainda estou pesquisando, mas me diga se estou caminhando para o lado errado. Aqui tem alguns exemplos que fui copiando por onde passei:

Byte 1: Start Byte ( 0 hexadecimal ).
Byte 2-3: ASCII Arduino's address.
Byte 4: Byte ENQ, ACK or NAK (0x05h, 0x06h y 0x15h) .
Byte 5: ASCII Requested command.
Byte 6 y 7: ASCII Function number.
Byte 8: Sign byte (Positive 0x20h y Negative 2D)
Byte 9-12: ASCII of data bytes (0x00h-0xFFFFh)
Byte 13: Byte EOT (End of Text) (0x03h)
Byte 14-15: Checksum (addition from 2nd byte to 13th byte)

O que eu tinha em mente, era enviar o valor (temperatura) como sendo a minha const char *msg, e no slave, decodificar o valor recebido de ASCII para números decimais.
Foi só uma ideia de antemão e sem fundamentos. Pelo jeito vou ter que quebrar um pouco mais a cabeça.

Olá... não sei se leste o que estava no link que coloquei... lá tem um pouco mais de informação e também explicações acerca do que será melhor.

Enviar em texto é muito bom para poderes testar coisas directamente dum terminal do computador, no entanto, para o microcontrolador dados em texto não é uma boa ideia.

Não sei como estás a calcular a temperatura, mas o meu projectinho está a fazer algo semelhante e o meu código/protocolo é este:

  float temp = getDS18B20();
  
  char toSend[5];   
  Serial.print("Now sending");//debug
  Serial.println(temp);//debug


  toSend[0] = 'T'; //header
  
  memcpy(&toSend[1], &temp, 4); // copy value. 

  bool ok = radio.write( toSend, MSG_LEN );

O meu protocolo é algo tão simples como T(temperatura em formato float).

O nRF já faz um CRC, logo não preciso de duplicar isso... e o nRF tem endereços também, logo não necessito de indicar qual o nó que está a enviar isto.

O meu sistema terá também pressão atmosférica, humidade, luminosidade e erros e todos eles funcionarão com base no que está em cima. Uma letra para definir a variável e 4 bytes de dados.

Para teres como testar, fica aqui o código de recepção.

      if (received[0] == 'T') { //Temperatura
        memcpy(&temperatura[pipe_num], &received[1], 4);
      }

Espero ter ajudado... nota que tu é que desenhas o protocolo. É bom ter por onde começar, mas convém ter algum sentido critico acerca do que já poderá estar implementado nos sistemas que estás a usar para simplificares o protocolo.
Outra coisa bastante discutida é se as mensagens necessitam de ter um feedback ou não... no caso da temperatura, creio que não é preciso nada disso uma vez que não há nada em termos de segurança que seja afectado.

No meu caso, também uso um float para a temperatura:

float temperatura = 0;

Para a leitura do valor, uso o analogRead e faço umas continhas para converter o valor, ou seja, calibrá-lo.

Mas meu protocolo é este:

void setup(){
  vw_setup(2000);
  vw_rx_start();
}
void loop(){
  char *msg;
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;

  valorLido = analogRead(pinoSensor);
  temperatura = (valorLido * 0.00488)*10;

   const char *msg = "4"; //Envia 4
   vw_send((uint8_t *)msg, strlen(msg));
   vw_wait_tx();

Como disse, o que pensei era trocar o 4 pelo valor analógico lido.
Não entendi muito bem como funciona o seu protocolo.

Pode ser perguntas bobas, mas o que seria:

[5] logo após o char toSend?

memcpy?

O que está no seu protocolo que é exclusivo do nRF?

Também não compreendo o código de recepção que me passaste.

Ok...

o memcpy, basicamente copia uma área de memória para outra. Eu dou-lhe o endereço da variável onde tenho guardado a temperatura, digo o tamanho (float tem 4 bytes), e para onde quero que ele copie que é o buffer de dados que vou enviar.

essas chavetas em frente ao toSend, servem para definir um array... se não sabes o que é um array, devias ver isso uma vez que é um dos tipos básicos do C. Mas basicamente, isso define 5 bytes de espaço.
Depois eu escrevo um T no primeiro byte e copio a minha variável float para os 4 bytes seguintes. Depois, é só enviar.

Sem nunca ter usado a virtualwire, diria que o código para fazer o mesmo que eu fiz seria algo assim:

void setup(){
  vw_setup(2000);
  vw_rx_start();
}


void loop(){
  unsigned char msg[4];
//não sei o que fazem estas duas linhas... mas não as estás a usar... 
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;

  valorLido = analogRead(pinoSensor);
  temperatura = (valorLido * 0.00488)*10;
  memcpy(msg, &temperatura, 4);
   vw_send((uint8_t *)msg, 4);
   vw_wait_tx();

do outro lado, fazes o inverso e deves receber a temperatura em float.

Hum.. Entendi, achei que era um array, mas não entendi porque 5.

Essas duas linhas:

  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;

funcionam para a recepção de um const char *msg da seguinte maneira:

if (vw_get_message(buf, &buflen))
         {
         int i;
         
         Serial.print("Recebido: ");
         
         for (i = 0; i < buflen; i++)
         {
         if(buf[i] == 'A'){
         statusFiltro = true;
         Serial.println("Recebeu A");
         }
         }
         }

Vou fazer alguns testes. Quanto ao code da recepção, seria algo desse tipo:

if (vw_get_message(buf, &buflen))
         {
         int i;
         
         Serial.print("Recebido: ");
         
         for (i = 0; i < buflen; i++)
         {
      if (buf[i] == '?') { //Temperatura
        memcpy(&temperatura[pipe_num], &buf[1], 4);
      }

Não, o código qque aqui meti não envia nenhum "?"

Sim, no seu caso, ele enviava o toSend, que correspondia ao 'T'
no meu caso, não sei o que por aí.

Ele envia isto:

  memcpy(msg, &temperatura, 4);
   vw_send((uint8_t *)msg, 4);
   vw_wait_tx();

mas o que ele deve ler na recepção?

unsigned char msg[4]; 
float temperatura = 0.0; 

unsigned char buflen = 4; 

if (vw_get_message(msg, &buflen)){
    memcpy(&temperatura, msg, 4); 
    Serial.print("Temperatura = ");
    Serial.println(temperatura);
}

Desculpe te fazer escrever o código de novo. Hoje a noite testo isso.

No mais, obrigado pela atenção.

Funcionou perfeitamente bubulindo. Obrigado. Vou deixar esse código salvo para que eu estude melhor quando chegar nesta etapa da linguagem C.

Com isso, posso deixar os sensores nos slaves, pensando em até, usar um LM35 por slave, e medir a diferença de temperatura entre os cômodos.

Muito obrigado pela ajuda. Vou estudar o código com mais detalhes. Quanto ao seu projeto, está tendo progresso? Lembro que me disse que estava avançando lentamente.

usar um LM35 por slave

A que distancia vai ficar o LM35 do teu micro?
Ja usei o LM35 e nao tive boas experiencias devido a distancia entre o LM35 e o avr.Visto ser analogico tive valores de leitura muito variaveis devido a interferencias no fio entre o LM e o ADC.Acabei mudando tudo para o DS18B20.Super estavel

Sim... algum progresso... mas não me tenho dedicado a ele.

Se vires no fórum existe um post acerca duma placa com nRF24 e ATtiny. Estou a ver como melhorar esse desenho e testando software em breadboard. Mas isto tem tempo... isto não tem de ficar feito já e serve para me ir entretendo.

Eu também iria pelo DS18B20... eu cheguei a ponderar o LM35 mas muita gente se queixa de não ser estável e depois de ver a gama e resolução, achei melhor o DS18B20.

Vou testar outros sensores de temperatura ainda.

Por agora, estarei tentando resolver o problema de comunicação, pois usando este sistema de envio do valor analógico, as string ('1', '2', ...) param de funcionar.

Fiz uns testes e vi que essa condição é que causa o problema:

if (vw_get_message(msg, &buff)){ // mudei de buf pra buff pra não ficar com mesmo nome
    memcpy(&temperatura, msg, 4); 
   // Serial.print("Temperatura = ");
    //Serial.println(temperatura);
}

Caso eu comente o mesmo, tudo volta a funcionar.

Logo após este if, vem o if que uso para ler as string:

if (vw_get_message(buf, &buflen)){

    int i;

   // Serial.print("Recebido: ");

    for (i = 0; i < buflen; i++)
    {
      if(buf[i] == '1'){
        digitalWrite(led, HIGH);
        //const char *Send = "A";
        //vw_send((uint8_t *)Send, strlen(Send));
        //vw_wait_tx();
        Serial.println("Entrou 1 e Saiu A");
      }

Quanto ao projeto, estou pensando em tirar a diferença entre os valores da temperatura e fazer um percentual para saber quantos % mais quente ou frio está determinada área.

Mais uma vez, o problema está no protocolo. Que é que definiste como as mensagens que vão ser enviadas? O meu exemplo mostra como fazer uma transmissão binária, mas é só isso. O protocolo tem de ser definido.
Já fizeste isso?

A variável buff, que é que contém? Devia ser o endereço do tamanho da mensagem a ler... Suponho que tenhas inicializado isso...

Off topic
Nao entendo o porquê de quem fez a lib virtual wire fez a função vw_get_message(msg, &buflen) com o parametro buflen passado como referencia e não por valor.O parâmetro msg faz todo o sentido ser passado por referencia já que irá ser alterado o seu conteúdo dentro da função e portanto é necessário indicar mos onde ela "vive" em memoria.Já o buflen passado desta forma penso que pode ter efeitos colaterais e não vejo sentido de passar o endereço da variável.
Em todo o caso isto em nada resolve o teu misterio :slight_smile:
Bons estudos

Eu pensei exactamente a mesma coisa... Ter de criar uma variável para ler um número constante de bytes é ridiculo... Mas pronto...

Quanto ao buff, tenho isso:

 char *Send;
  unsigned char msg[4]; 
  unsigned char buff = 4;  

  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;

 
/*
if (vw_get_message(msg, &buff)){
    memcpy(&temperatura, msg, 4); 
   // Serial.print("Temperatura = ");
    Serial.println(temperatura);
}
*/

E o que estás a enviar?

Fizeste o exercício que sugeri de escreveres o teu protocolo e as mensagens que serão enviadas?