Manual

do

Maker

.

com

Acionar relé com Whisper Node

Acionar relé com Whisper Node

Recentemente escrevi o artigo de apresentação do Talk² Whisper Node e em um curto espaço de tempo escrevi o segundo artigo, já esse introdutório. Se não leu e ainda não conhece esse fabuloso hardware, leia os artigos anteriores para ver as especificações e recursos, simplesmente clicando nos links desse parágrafo.

Talk2whisperNode.webp

Nesse artigo de hoje vou mostrar um pouco da comunicação entre os Whisper Node, utilizando um GPIO (que obviamente você já utilizou aos montes) para acionar um relé de canal duplo.. Após esse artigo, você será capaz de fazer tudo com o Whisper Node, bastando trocar informações via RF. Decidi fazê-lo porque me perguntaram sobre o modo de acionar relé que no final das contas, é igual qualquer Arduino, então percebi que a dúvida que paira é na camada de rádio.

Sobre o relé de canal duplo

rele-gbk_robotics.webp

Um relê de canal duplo oferece saída para um segundo borne, de modo que você fica com 2 COM, 2 NO e 2 NC. Se não tem intimidade com relés ainda, sugiro esse artigo onde discorro a respeito. Escrevi diversos outros artigos sobre relé, mas esse video é mais teórico.

Nesse video não falei do relé de canal duplo porque não o tinha. Se tiver interesse nele, visite a loja da GBK Robotics e divirta-se.

A carga sobre esse modelo pode ser 2A em 24V ou 1A em 120V. A carga máxima é de 50VA; 30W. Sua sensibilidade é ótima, 1V@1mA para acionamento.

Seleção da board

No artigo anterior relacionado, mostrei como preparar a IDE do Arduino para utilizar a Whisper Node. Leia-o para saber o quão simples é todo o processo.

Configurações do rádio

O sketch default de qualquer exemplo já tem as configurações pre-definidas, de modo que você não precisará nunca decorar essas instruções. Destas, você deve dar atenção às seguintes:

#define RADIO_FREQUENCY 916.0

No artigo anterior relacionado, eu deixei uma tabela relacionada. No caso, esse 916.0 é a frequência que deve ser configurada para o rádio de 915MHz; default do sketch e o que estou utilizando para escrever esse artigo.

#define RADIO_TX_POWER 13

Dois valores são aplicáveis; 13 para baixo consumo, 14 para normal. Eu não experimentei com 14, mas provavelmente farei testes tão logo crie uma condição que o propicie.

Por fim, selecionar os endereços do base e do node; receptor e emissor, respectivamente. No sketch padrão você encontrará a seguinte configuração (que poderá inclusive deixar como está):

#define nodeAddr 0x88
#define baseAddr 0x0B

 

setup()

No setup de qualquer exemplo você vê a inicialização do rádio, modo de configuração, ajuste de frequência, encriptação do canal e ajuste de potência. Tudo simples assim:

void setup()
{
  // Serial
  Serial.begin(115200);
  Serial.println(F("Voltage - Base"));

  // Radio
  myRadio.init();
  myRadio.setModemConfig(RH_RF69::FSK_Rb125Fd125);
  myRadio.setFrequency(RADIO_FREQUENCY);
  uint8_t myRadioEncryptionKey[] = RADIO_ENCRYPTION_KEY;
  myRadio.setEncryptionKey(myRadioEncryptionKey);
  myRadio.setTxPower(RADIO_TX_POWER);
...

A chave não está fechada porque tem mais conteúdo em setup(). Esses valores são iguais em ambos; o 'node' e o 'base'. No caso, o 'base' está recebendo a mensagem.

loop()

Em loop, o exemplo "Voltage.base" utiliza uma função runReceiver(). Essa função está mastigada para que você não tenha o menor trabalho em receber dados.

Enquanto isso no 'Voltage.base'...

E no sketch que envia, você vê mais  e mais dos métodos prontos do Talk2. Realmente fica fácil brincar (ou trabalhar) com um hardware bom que tenha uma boa biblioteca, como é o caso dessa delícia. Na função sendTestVoltage(...) você vê uma chamada de um método da classe T2Utils, que é a readVoltage(...). Dá pra ser mais fácil que isso? Eu duvido.

Depois que o valor é lido para uma variável, formata-se a mensagem para o envio e acontece a chamada myRadio.send(...).

Porque tanta explicação?

Se você abrir o sketch, pode ficar desanimado se não tiver essa introdução. Digo isso porque você verá diversas chamadas da própria biblioteca e todas elas são conceitos a serem adquiridos, mas você pode de imediato passar a utilizar o Talk2 Whisper Node e absorver os recursos extras oferecidos posteriormente. Tudo exclarecido, vamos então aos testes.

Primeiro teste - suba os sketch padrão

Antes de fazer qualquer coisa diferente, suba os sketches default em cada um dos dispositivos. É importante que você os veja operando normalmente para depois ter absoluta de certeza de que qualquer comportamento anômalo estará sendo causado por um erro de programação, não do dispositivo.

Conecte todos os fios do FTDI, de outro modo não funcionará - isto é, não conecte apenas DTX, DRX, GND e VCC; conecte todos!

Se utilizar o FTDI dessa imagem acima, basta conectar na ordem; pino 1 ao pino 1, pino 2 ao pino 2 e assim por diante. Atente-se apenas em não iniciar o wiring com as direções invertidas. Na parte de trás do Whisper Node tem os nomes dos headers. Coloque o GND ao GND do FTDI, depois basta seguir a sequência.

Tendo confirmado o funcionamento, uma leve modificação no código incrementará a mensagem para por exemplo, acionar um relé. O define T2_MESSAGE_MAX_DATA_LEN mantém a mensagem em 8 bytes. Não experimentei ainda mexer na biblioteca, mas com esses 8 bytes por mensagem já dá pra brincar um bocado. Mas sem pensar em escrever um código completo por enquanto, vamos apenas adicionar um byte na mensagem.

A última informação importante para o momento é que o LED azul está sendo utilizado para a comunicação do rádio e o LED ambar é local.

// Prepare Message (os headers)
  myMsg.cmd = 0x00;
  myMsg.idx = 0x06;
  myMsg.src = nodeAddr;
  myMsg.dst = baseAddr;
  myMsg.data[0] = 0x01; // Operation Last Reading
  myMsg.data[1] = 0x01; // Battery/Supply Index, if multiple supplies
  myMsg.data[2] = voltage >> 8;
  myMsg.data[3] = voltage;
  myMsg.data[4] = 0;
  myMsg.data[5] = 0; //vou utilizar esse byte 

Repare que estou utilizando o quinto byte da mensagem formatada, assim não preciso nem escrever código. Em um próximo artigo mostrarei uma comunicação notebook a notebook, de forma a ter uma rede de comunicação de longa distância exclusiva. Pense que com esse único byte você poderá definir mais chamadas do que a memória do dispositivo pode suportar e para enviar comandos remotos é perfeito. Apenas para exemplificar isso, poderia ser por exemplo um relé duplo (não um relé de canal duplo). A definição de myMsg.data[5] será '1' para o relé 1, '2' para o relé 2, '3' para os 2 relés e '0' para desligar ambos. Vamos implementar isso no quinto Byte e implementar também a recepção serial de um valor para o relé. Além disso, precisamos definir um I/O para o relé, sendo os pinos D4 e D5 (o D5z será fictício, uma vez que estou exemplificando com um relé apenas).

Falei com o Mike (o criador da jóia) e pedi algumas informações, pois eu estava tratando de ler todas as classes disponíveis, mas sequer foi necessário ler tudo para terminar esse post, ele me deu uma ótima orientação e, no final das contas, decidi que esse dispositivo merece mais alguns artigos devido às possibilidades e em um desses artigos, vou tentar mostrar uma comunicação de uma distância muito, muito longa. Além disso pretendo mostrar em outros artigos  os recursos já implementados na biblioteca do Talk2, o de hoje é para mostrar apenas o  acionamento do relé juntamente com o recurso que o próprio sketch oferece, então sigamos.

Voltage.node

Mais uma vez, estou utilizando esse sketch para rechear com mais informações.

No Voltage.node apenas adicionamos um byte extra na mensagem (apesar de o ideal ser passar o endereço do relay e o valor). Tirei o header do código apenas para reduzir o tamanho, mas esse é o sketch de exemplo default acrescido de algumas linhas de comando para a comunicação pretendida:

#include <T2WhisperNode.h>
#include <RH_RF69.h>

// LED
T2Led myLedBlue(T2_WPN_LED_1);
T2Led myLedYellow(T2_WPN_LED_2);

int value = 0;

// RFM69 Radio
#define RADIO_FREQUENCY 916.0
#define RADIO_TX_POWER 13
#define RADIO_ENCRYPTION_KEY { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }
RH_RF69 myRadio;
uint8_t radioBuf[(T2_MESSAGE_HEADERS_LEN + T2_MESSAGE_MAX_DATA_LEN)];

// T2 Message
T2Message myMsg;
#define nodeAddr 0x88
#define baseAddr 0x0B

// T2 Data Buffer
T2DataBuffer myDataBuffer;

// Stand-alone Task
void taskVoltageFunction(T2Task * Task); // Implemented at the bottom of this file
T2Task myTaskVoltage(&taskVoltageFunction);

void setup()
{
  // Serial
  Serial.begin(115200);
  //Serial.println(F("Voltage - Node"));

  // Radio
  myRadio.init();
  myRadio.setModemConfig(RH_RF69::FSK_Rb125Fd125);
  myRadio.setFrequency(RADIO_FREQUENCY);
  uint8_t myRadioEncryptionKey[] = RADIO_ENCRYPTION_KEY;
  myRadio.setEncryptionKey(myRadioEncryptionKey);
  myRadio.setTxPower(RADIO_TX_POWER);

  // Stand-alone Task
  myTaskVoltage.runInterval(1000);   // Task will tun every 1000 milliseconds
  myTaskVoltage.runCount(0);         // Task will run forever
  myTaskVoltage.status(T2TASK_STATUS_SCHEDULED);

  // LED Example
  //myLedBlue.setBlink(20, 500, -1);   // LED will lit for 20ms every 250ms, forever
}

void loop()
{
  if (Serial.available() > 0){
    char incoming = '8';
    Serial.println("escolha o rele a acionar:");
    Serial.println("0 - desligar tudo");
    Serial.println("1 - ligar o primeiro");
    Serial.println("2 - ligar o segundo");
    Serial.println("3 - liga ambos");
    
  
    Serial.readBytes(&incoming,1);
    Serial.println("----");
    Serial.println(incoming);
    Serial.println("----");

    if (incoming-48 >-1 && incoming-48 <4){
        value = incoming-48;
    }
    
    Serial.println("value: no read: ");
    Serial.println(value);
    value = value < 4 ? value : 0;
    Serial.flush();
  }

     
  // Stand-alone Task - We run it as a non-blocking Task
  myTaskVoltage.run();

  // LED Example
  myLedBlue.run();
  myLedYellow.run();
}

void taskVoltageFunction(T2Task * Task)
{
  //Serial.println(F("TaskRunning"));

  // Se the Yellow LED to blink, indicating are doing something
  myLedYellow.setBlink(10, 0, 1); // LED will lit for 10ms only once, so the interval doesn't matter
  
  sendTestVoltage(1);
  sendTestVoltage(2);
}

void sendTestVoltage(uint8_t supply)
{
  uint16_t voltage = 0;
  //int voltage = 't';

  // Get Voltage readings from supply/battery
  if(supply == 1)
  {
    voltage = T2Utils::readVoltage(T2_WPN_VBAT_VOLTAGE, T2_WPN_VBAT_CONTROL);
    myMsg.sdx = 0x64; // Battery
  }

  if(supply == 2)
  {
    voltage = T2Utils::readVoltage(T2_WPN_VIN_VOLTAGE, T2_WPN_VIN_CONTROL);
    myMsg.sdx = 0x65; // Supply
  }

  uint8_t radioBufLen = 0;

  // Prepare Message
  myMsg.cmd = 0x00;
  myMsg.idx = 0x06; //grupo da mensagem
  myMsg.src = nodeAddr;
  myMsg.dst = baseAddr;
  myMsg.data[0] = 0x01; // Operation Last Reading
  myMsg.data[1] = 0x01; // Battery/Supply Index, if multiple supplies
  myMsg.data[2] = voltage >> 8;
  myMsg.data[3] = voltage;
  myMsg.data[4] = 0;
  myMsg.data[5] = value;
  
  myMsg.len = 6;
  //myMsg.len = 11; //Update length
  Serial.print("valor a enviar: ");
  Serial.println(myMsg.data[5]);
  Serial.println(value);
  
  // Encode Message and get the full length
  myMsg.getSerializedMessage(radioBuf, &radioBufLen);
  
  // Send it
  myRadio.send(radioBuf, radioBufLen);
  myRadio.waitPacketSent(100);
}

Suba o sketch para o Talk2 Whisper Node. Apenas o LED ambar estará se acendendo de forma intermitente. Posteriormente (durante a comunicação) o LED azul se acenderá e você perceberá que a comunicação está acontecendo. Não será o caso desse exemplo, mas em minhas tentativas e erro eu percebi que minha recepção estava errada porque a comunicação acontecia, só que eu não estava conseguindo tratar a recepção. Como eu queria debugar visualmente, adquiri um novo FTDI e demorei um pouco mais para publicar esse artigo, devido à demora da chegada.

Voltage.base

O envio da mensagem é feito periodicamente porque estou utilizando o sketch padrão para enviar as informações de tensão do Whisper Node, mas a informação do relé é recebida pela comunicação serial. Isto significa que a qualquer momento que você digitar um dos valores aceitáveis, ele substituirá o valor original da inicialização do sketch (que é '0', para os relés desligados) e passará a enviá-lo constantemente via rádio. Aqui no Voltage.base teremos uma memória de estado, isto é,  se o novo comando para o relé for o mesmo que o anterior, simplesmente descarta a interação. Repare que na linha 155 tem uma chamada de debug, que me ajudou de imediato a perceber o que estava acontecendo. Após utilizada (isto é, se desejado) volte a comentá-la.

/*
 * Talk2 Example: Voltage - Base
 * http://talk2.wisen.com.au
 *
 * This example demonstrate how to receive a message and print on the Serial.
 * The message to be received is the voltage readings from the Power Supply
 * and the Battery, sent by a remote node.
 * 
 * - This example works together with "Voltage - Node"
 * - The messages are formatted using the Talk2 Message frame
 * - Remember to adjust the frequency and TX Power to match your board
 *
 *  Copyright 2015-2016 by Mike Musskopf.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */
#include <T2WhisperNode.h>
#include <RH_RF69.h>

#define RELAY_1 4
#define RELAY_2 5

int last = 8;

// LED
T2Led myLedBlue(T2_WPN_LED_1);
T2Led myLedYellow(T2_WPN_LED_2);

// RFM69 Radio
#define RADIO_FREQUENCY 916.0
#define RADIO_TX_POWER 13
#define RADIO_ENCRYPTION_KEY { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }
RH_RF69 myRadio;
uint8_t radioBuf[(T2_MESSAGE_HEADERS_LEN + T2_MESSAGE_MAX_DATA_LEN)];

// T2 Message
T2Message myMsg;
#define nodeAddr 0x88
#define baseAddr 0x0B

// T2 Data Buffer
T2DataBuffer myDataBuffer;

// Stand-alone Task
void taskPrintFunction(T2Task * Task); // Implemented at the bottom of this file
T2Task myTaskPrint(&taskPrintFunction);

// Global Variables to hold the Received data
uint16_t batVoltage = 0;
uint16_t batVoltageCounter = 0;
uint16_t supVoltage = 0;
uint16_t supVoltageCounter = 0;

void setup()
{
  // Serial
  Serial.begin(115200);
  Serial.println(F("Voltage - Base"));

  pinMode(RELAY_1,OUTPUT);
  pinMode(RELAY_2,OUTPUT);
  digitalWrite(RELAY_1,LOW);
  digitalWrite(RELAY_2,LOW);

  // Radio
  myRadio.init();
  myRadio.setModemConfig(RH_RF69::FSK_Rb125Fd125);
  myRadio.setFrequency(RADIO_FREQUENCY);
  uint8_t myRadioEncryptionKey[] = RADIO_ENCRYPTION_KEY;
  myRadio.setEncryptionKey(myRadioEncryptionKey);
  myRadio.setTxPower(RADIO_TX_POWER);

  // Stand-alone Task
  myTaskPrint.runInterval(1000);   // Task will tun every 1000 milliseconds
  myTaskPrint.runCount(0);         // Task will run forever
  myTaskPrint.status(T2TASK_STATUS_SCHEDULED);

  // LED Example
  myLedYellow.setBlink(20, 3000, -1);   // LED will lit for 20ms every 3000ms, forever
}

void loop()
{
  // Stand-alone Task - We run it as a non-blocking Task to print the readings
  myTaskPrint.run();

  // LED Example
  myLedBlue.run();
  myLedYellow.run();
  
  // Run the Receiving function
  runReceiver();
}

int relayController(int val){
  Serial.print("Val DENTRO0: ");
  Serial.println(val);

  if (val < 0 || val > 3){
    return 0;
  }
  
  last = val;

  if (val == 0){
      digitalWrite(RELAY_1,LOW);
      digitalWrite(RELAY_2,LOW);
      Serial.println("Comando 0");
    }
    else if (val == 1){
      digitalWrite(RELAY_2,LOW);
      digitalWrite(RELAY_1,HIGH);
      Serial.println("Comando 1");
    }
    else if (val == 2){
      digitalWrite(RELAY_1,LOW);
      digitalWrite(RELAY_2,HIGH);
      Serial.println("Comando 2");
    }
    else if (val == 3){
      digitalWrite(RELAY_2,HIGH);
      digitalWrite(RELAY_1,HIGH);
      Serial.println("Comando 3");
    }
    return 0;
}

void runReceiver()
{
  /* RFM69 Receive */
  uint8_t radioBufLen = sizeof(radioBuf);
  if(myRadio.recv(radioBuf, &radioBufLen))
  {
    myMsg.setSerializedMessage(radioBuf, radioBufLen);
    Serial.println("-----------------------");
    // Uncomment bellow to print every received message, just be careful as
    // delays here can cause messages to be lost.
    myMsg.printMessage();
    Serial.println(myMsg.data[5]);
    if (myMsg.data[5] > -1 && myMsg.data[5] < 4){
      relayController(myMsg.data[5]);
    }
    Serial.println("------------------------");

    // We can perform some message filtering based on it's headers
    if(myMsg.idx == 0x06 && myMsg.src == nodeAddr && myMsg.dst == baseAddr)
    {

      // Lets blink something
      myLedBlue.setBlink(10, 0, 1); // LED will lit for 10ms only once, so the interval doesn't matter

      Serial.print("Valor da msg no receiver: ");
      Serial.println(myMsg.data[4]);
      int value = myMsg.data[4]-48;
      Serial.print("Valor subtraido: ");
      Serial.println(value);
      relayController(value);
        
      switch(myMsg.sdx)
      {
      case 0x64: // Battery Voltage
        // Increment Counter
        batVoltageCounter++;
        // Concatenate 2 bytes into a uint16_t variable
        batVoltage = myMsg.data[2] << 8;
        batVoltage |= myMsg.data[3];
        break;

      case 0x65: // Power Supply Voltage
        // Increment Counter
        supVoltageCounter++;
        // Concatenate 2 bytes into a uint16_t variable
        supVoltage = myMsg.data[2] << 8;
        supVoltage |= myMsg.data[3];
        break;
        
      default: // Can define an operation for everything else
        // Do something
        Serial.println(F("Unexpected message received: "));
        myMsg.printMessage();
        break;
      }
    }
  }
}

void taskPrintFunction(T2Task * Task)
{
  // We simply print the last data we got
  //Serial.println(F("### Latest Voltage Readings ###"));
  //Serial.print(F("       Battery: ")); Serial.print(batVoltage); Serial.print(F(" millivolts. ")); Serial.println(batVoltageCounter);
  //Serial.print(F("  Power Supply: ")); Serial.print(supVoltage); Serial.print(F(" millivolts. ")); Serial.println(supVoltageCounter);
  Serial.println();
}

Mais uma vez devo enfatizar que você pode alimentar a board com um par de pilhas em um adaptador como o mostrado no artigo anterior. Aqui estou com ambas as boards conectadas aos FTDIs para poder mostrar pra você também a troca de mensagem para o acionamento do relé.

Obviamente você não poderá utilizar a IDE do Arduino para visualizar mensagem serial em ambas, mas em uma delas sim. Na outra, você pode utilizar o programa "screen", "minicom" "gtkterm" ou outro que desejar. Eu gosto do GTKTerm e é ele que estou utilizando no video.

 

A conexão com o relé é o padrão de sempre; COM, NO, NC. COM é a tensão, "NO" é "normalmente aberto" e "NC" é "normalmente fechado". Vou acender um LED com o relé apenas para exemplificar.

Inscreva-se no nosso canal Manual do Maker no YouTube.

Também estamos no Instagram.

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.