Trying to send temperature and humidity in one RF message

Hello,

I am trying to make a code to send DHT22 measured temperature and humidity in one message over the 433mhz radio to a receiving arduino node.

Here are the integers that hold the temperature and humidity:

int16_t t = dht.readTemperature(false, true) * 100;
uint16_t h = dht.readHumidity() * 100;

Here is the RF message construction where i try to fit both temp and hum to the myMsg.data:

// Prepare Temperature Message and send
  myMsg.cmd = 0x03; // Return Data
  myMsg.idx = 0x05; // HID and Sensors
  myMsg.sdx = 0x0a; // Temperature
  myMsg.data[0] = 0x01; // Operation Temp in Celcius * 100
  myMsg.data[1] = 0x01; // Sensor ID - we only have 1 in this case
  myMsg.data[2] = t >> 4;
  myMsg.data[2] = h >> 4;
  myMsg.data[3] = t;
  myMsg.data[3] = h;
  myMsg.len = 8; // Update length
  sendMessage();

On the receiving end I get:

  ------- MSG Dump: START -------
Headers: HEX [DEC]
    Idx: 5 [5]
    Cmd: 3 [3]
    Sdx: A [10]
    Src: 91 [145]
    Dst: C [12]
    Rtr: 0 [0]
 Length: 8
1:1:BB:B8:0:0:0:0:
-------- MSG Dump: END --------

I think I have some fundamental error on this and also I'm not understanding the bit shifting here enough to make this work.

The goal is to have the temperature first and then the humidity appended like this in the MSG: (eg. 23003510). 23.00 being temperature and 35.10 being the humidity. Also there could be a delimiter "," between them to make things clearer if thats possible... Any help on this?

Post your complete code, both TX and RX.

  myMsg.data[2] = t >> 4;
  myMsg.data[2] = h >> 4;
  myMsg.data[3] = t;
  myMsg.data[3] = h;

Explain what you think this is doing. It isn't, but I could use a good laugh.

PaulS:

  myMsg.data[2] = t >> 4;

myMsg.data[2] = h >> 4;
  myMsg.data[3] = t;
  myMsg.data[3] = h;



Explain what you think this is doing. It isn't, but I could use a good laugh.

Someone just asked your help and this is how you reply? Nice effort.

The TX code:

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

// DHT 22
#define DHT_PWD_PIN A2 // We power the DHT11 via a MCU GPIO so we can control when it's up or not
#define DHT_DATA_PIN A1
#define DHT_TYPE DHT22
DHT dht(DHT_DATA_PIN, DHT_TYPE);

// SPI Flash
T2Flash myFlash;

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

// T2 Message
T2Message myMsg;
#define nodeAddr 0x91
#define baseAddr 0x0C

void setup()
{
  // Serial
  Serial.begin(115200);
  Serial.println(F("DHT22 Sensor Node"));

  Serial.println(F("Putting Radio and SPI Flash to Sleep"));
  // Radio - Initialize the radio and put it to sleep to save energy
  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);
  myRadio.sleep();

  // Flash - We're not using, so just power it down to save energy
  myFlash.init(T2_WPN_FLASH_SPI_CS);
  myFlash.powerDown();

  // DHT22
  //digitalWrite(DHT_PWD_PIN, LOW);
  //pinMode(DHT_PWD_PIN, OUTPUT);
  //dht.begin();

  // Setup the Blue LED pin
  digitalWrite(T2_WPN_LED_1, LOW); // Set LED to Off
  pinMode(T2_WPN_LED_1, OUTPUT);   // Set LED pint to OUTPUT

  // Blink the blue led once at the boot
  digitalWrite(T2_WPN_LED_1, HIGH);
  delay(5);
  digitalWrite(T2_WPN_LED_1, LOW);

}

uint8_t loopCount = 2;

void loop()
{

  // We only do something every 2 sleep cycles
  if(loopCount == 2)
  {

    digitalWrite(DHT_PWD_PIN, LOW);
    pinMode(DHT_PWD_PIN, OUTPUT);
    dht.begin();
    // Do some work!
    sendTempHumidity();
    loopCount = 0;

  }

  // Using Low-Power library to put the MCU to Sleep
  LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
  loopCount++;

}

void sendTempHumidity()
{
  // Turn the Sensor ON
  digitalWrite(DHT_PWD_PIN, HIGH);

  //Wait a bit for the sensor to wake up
  delay(800);

  // Read temperature as Celsius * 100 to remove any decimals
  // False, True means we don't want Fahrenheit and we want to force the reading.
  int16_t t = dht.readTemperature(false, true) * 100;

  // * 100 to remove any decimals. We do not force the reading, so we use the last info
  uint16_t h = dht.readHumidity() * 100;


  //Turn the Sensor OFF
  digitalWrite(DHT_PWD_PIN, LOW);
  pinMode(DHT_DATA_PIN, INPUT);
  pinMode(DHT_PWD_PIN, INPUT);
  
  
  // For Debbuging only
  //Serial.println(t);
  //Serial.println(h);
  //delay(10);


  // Prepare Temperature Message and send
  myMsg.cmd = 0x03; // Return Data
  myMsg.idx = 0x05; // HID and Sensors
  myMsg.sdx = 0x0a; // Temperature
  myMsg.data[0] = 0x01; // Operation Temp in Celcius * 100
  myMsg.data[1] = 0x01; // Sensor ID - we only have 1 in this case
  myMsg.data[2] = t >> 4;
  myMsg.data[2] = h >> 4;
  myMsg.data[3] = t;
  myMsg.data[3] = h;
  myMsg.len = 8; // Update length
  sendMessage();



}


void sendMessage()
{
  uint8_t radioBufLen = 0;

  // Prepare the Message headers
  myMsg.src = nodeAddr;
  myMsg.dst = baseAddr;

  // Encode Message and get the full length
  myMsg.getSerializedMessage(radioBuf, &radioBufLen);

  // Send it
  myRadio.send(radioBuf, radioBufLen);
  myRadio.waitPacketSent(100);
  myRadio.sleep();

}

And here is the RX code:

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

String inputString = "";         // a String to hold incoming data        
boolean stringComplete = false;  // whether the string is complete

//Define bedroom variables
float bedroomtemp;
float bedroomhum;
int bedroombatvolt;
int bedroomsupvolt;


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

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

// T2 Message
T2Message myMsg;

//Define node addresses
#define nodeBedRoom 0x91
#define baseAddr 0x0C

// T2 Data Buffer
T2DataBuffer myDataBuffer;

void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    inputString.trim();
    // if the characters get recognized jump to main code and execute printing
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}
void setup()
{
  // Serial
  Serial.begin(115200);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
  Serial.println(F("Probe base node for receiving data from probes:"));

  // 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);

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

void loop()
{
if (stringComplete) {

if (inputString == "getbedroom") {
      Serial.print("bedroomth: ");
      Serial.print(bedroomtemp/100.0, 2);
      Serial.print(',');
      Serial.print(bedroomhum/100.0, 2);
      Serial.print(" bedroombatvolt: ");
      Serial.print(bedroombatvolt);
      Serial.print("mV");
      Serial.print(" bedroomsupvolt: ");
      Serial.print(bedroomsupvolt);
      Serial.println("mV");
    }
// clear the string:
    inputString = "";
    stringComplete = false;
  }

  // LED Example
  myLedBlue.run();
  myLedYellow.run();
  
  // Run the Receiving function
  runReceiver();
}
void runReceiver()
{
  /* RFM69 Receive */
  uint8_t radioBufLen = sizeof(radioBuf);
  if(myRadio.recv(radioBuf, &radioBufLen))
  {
    myMsg.setSerializedMessage(radioBuf, radioBufLen);
    // The next is for debugging only to print every message. Uncomment bellow to print every received message, just be careful as
    // delays here can cause messages to be lost.
    myMsg.printMessage();

    // If the message is initiated with sdx = 0x0a we know this is the temperature message
    if(myMsg.idx == 0x05 && myMsg.sdx == 0x0a && myMsg.src == nodeBedRoom && myMsg.dst == baseAddr)
    {
      
      // Lets blink something
      myLedBlue.setBlink(10, 0, 1); // LED will lit for 10ms only once, so the interval doesn't matter
      
      switch(myMsg.sdx)
      {
      default:

        // Read the part from the message that holds the temp reding and save it to a variable
        bedroomtemp = myMsg.data[2] * 256 + myMsg.data[3];
        break;
      }
    }

   // If the message is initiated with sdx = 0x0b we know this is the humidity message
   else if(myMsg.idx == 0x05 && myMsg.sdx == 0x0b && myMsg.src == nodeBedRoom && myMsg.dst == baseAddr)
    {
      
      // Lets blink something
      myLedBlue.setBlink(10, 0, 1); // LED will lit for 10ms only once, so the interval doesn't matter
      
      switch(myMsg.sdx)
      {
      default:

        // Read the part from the message that holds the hum reding and save it to a variable
        bedroomhum = myMsg.data[2] * 256 + myMsg.data[3];
        break;
      }
    }
    // If the message has idx = 0x06 we know that this is the voltage readings from probe node
    else if(myMsg.idx == 0x06 && myMsg.src == nodeBedRoom && myMsg.dst == baseAddr)
    {
      
      // Lets blink something
      myLedBlue.setBlink(10, 0, 1); // LED will lit for 10ms only once, so the interval doesn't matter

      switch(myMsg.sdx)
      {
      case 0x64: // Battery Voltage
        // Concatenate 2 bytes into a uint16_t variable
        bedroombatvolt = myMsg.data[2] << 8;
        bedroombatvolt |= myMsg.data[3];
        break;
case 0x65: // Power Supply Voltage
        // Concatenate 2 bytes into a uint16_t variable
        bedroomsupvolt = myMsg.data[2] << 8;
        bedroomsupvolt |= myMsg.data[3];
        break;
        
      }
      
    }
  
      }
    }
  }
}

And what comes to this weird way of filling the message on the TX end:

 // Prepare Temperature Message and send
  myMsg.cmd = 0x03; // Return Data
  myMsg.idx = 0x05; // HID and Sensors
  myMsg.sdx = 0x0a; // Temperature
  myMsg.data[0] = 0x01; // Operation Temp in Celcius * 100
  myMsg.data[1] = 0x01; // Sensor ID - we only have 1 in this case
  myMsg.data[2] = t >> 4;
  myMsg.data[2] = h >> 4;
  myMsg.data[3] = t;
  myMsg.data[3] = h;
  myMsg.len = 8; // Update length
  sendMessage();

Yep it does not make any sense but could this work by just making the message longer and having temperature and humidity in their own “data slots” and then on the receiving end just read both and multiply myMsg.data[2] and myMsg.data[3] by 256 to get the decimal values?:

 // Prepare Temperature Message and send
  myMsg.cmd = 0x03; // Return Data
  myMsg.idx = 0x05; // HID and Sensors
  myMsg.sdx = 0x0a; // Temperature
  myMsg.data[0] = 0x01;
  myMsg.data[1] = 0x01;
  myMsg.data[2] = t >> 8; //temperature
  myMsg.data[3] = h >> 8; //humidity
  myMsg.len = 4; // Update length
  sendMessage();

Someone just asked your help and this is how you reply? Nice effort.

You wrote the code that way. You should be able to explain why. "I don't really know what I'M doing" is a perfectly acceptable thing to say. Dismissing any attempts to elicit more information is not.

could this work by just making the message longer and having temperature and humidity in their own "data slots" and then on the receiving end just read both and multiply myMsg.data[2] and myMsg.data[3] by 256 to get the decimal values?

Yes, and no. You need 4 bytes in the array to hold the 4 bytes you need to send and receive. But, you are not sending decimal values. That implies floating point variables, not ints.

You can convert the floats you have to ints, as you are doing, use highByte() and lowByte(), or bitshifting and masking, to convert the signed and unsigned ints to bytes, stuff the bytes into the 4 elements of the array, send and receive the array, and then use bitshifting, or multiply by 256 and addition to recreate the ints that you can then convert back to floats.

    digitalWrite(DHT_PWD_PIN, LOW);
    pinMode(DHT_PWD_PIN, OUTPUT);

You only need to set 'pinMode()' ONCE, not every time you access the pin. Do it in the 'setup()' code, before the first access. Then don't do it again.

You need 2 bytes for each 16 bit integer. Try something like this on the TX side, reverse on the RX. Be sure you have the message length correct.

  // Use better name. I prefer an explicit cast
  int16_t currentTemp = (uint16_t)(dht.readTemperature(false, true) * 100.0);

  // Use better name. I prefer an explicit cast.
  uint16_t currentHumid = (uint16_t)(dht.readHumidity() * 100.0);

  memcpy(myMsg.data + 2, &currentTemp, 2);
  memcpy(myMsg.data + 4, &currentHumid, 2);
  int16_t currentTemp = (uint16_t)(dht.readTemperature(false, true) * 100.0);

I don't think explicitly casting the float to a uint16_t, when the temperature COULD be negative, is a good idea. Or even necessary. The compiler knows how to truncate a float to fit in an int.

PaulS:

  int16_t currentTemp = (uint16_t)(dht.readTemperature(false, true) * 100.0);

I don't think explicitly casting the float to a uint16_t, when the temperature COULD be negative, is a good idea. Or even necessary. The compiler knows how to truncate a float to fit in an int.

My bad. Meant to cast (int16_t). But, I believe you on the compiler being smart enough.

PaulS:
You need 4 bytes in the array to hold the 4 bytes you need to send and receive. But, you are not sending decimal values. That implies floating point variables, not ints.
You can convert the floats you have to ints, as you are doing, use highByte() and lowByte(), or bitshifting and masking, to convert the signed and unsigned ints to bytes, stuff the bytes into the 4 elements of the array, send and receive the array, and then use bitshifting, or multiply by 256 and addition to recreate the ints that you can then convert back to floats.

Got it working now with your advice PaulS. Thank you so much! Now by sending temp and humidity on the same message I can save some current from my battery because I do not have to do a separate transmit with the humidity reading.

I did this on the TX side when constructing the RF message:

  myMsg.cmd = 0x03; // Return Data
  myMsg.idx = 0x05; // HID and Sensors
  myMsg.sdx = 0x0a; // Temperature & humidity
  myMsg.data[0] = highByte(t);
  myMsg.data[1] = lowByte(t);
  myMsg.data[2] = highByte(h);
  myMsg.data[3] = lowByte(h);
  myMsg.len = 4; // Update length
  sendMessage();

And this on the RX side to pick up the values for logging later on to an external RRD database:

kitchentemp = myMsg.data[0] * 256 + myMsg.data[1];
kitchenhum = myMsg.data[2] * 256 + myMsg.data[3];

Cheers!