Notifications
Clear all

Structures, Serialisation, XOR Checksums etc.

91 Posts
7 Users
8 Likes
20.6 K Views
Pugwash
(@pugwash)
Noble Member
Joined: 4 years ago
Posts: 1012
Topic starter  

This is a follow-on thread from BME/BMP 280.

With much-appreciated advice and help from @frogandtoad, I have managed to get the following code to work as expected.

typedef struct{
  float temperature;
  float pressure;
  float altitude;
  float humidity;
  byte checksum;  
  }bme280data;

bme280data myObject; // declare the message object 17 bytes long

void setup() {
  
  // some nice fictitious values for testing 
  myObject.temperature = 22.34;
  myObject.pressure = 1005.43;
  myObject.altitude = 25.05;
  myObject.humidity = 46.75;
  
  
  Serial.begin(9600);
  Serial.println("Starting...");
  
  myObject.checksum = checkSum(); // calculate checksum from first 16 bytes and append
    
  printEverything(); //prints individual bytes for testing purposes
  
// prints the checksum Serial.print("Checksum = ");Serial.print(checkSum(), HEX); Serial.println(" HEX"); } void loop() {  // put your main code here, to run repeatedly: } int checkSum(){    unsigned char *buffer=(char*)malloc(sizeof(myObject));  memcpy(buffer,(const unsigned char*)&myObject,sizeof(myObject));  int result = buffer[0];  for(int i=1; i<sizeof(myObject) - 1; i++){    result ^= buffer[i];  }    free(buffer);  return result;  } void printEverything(){    unsigned char *buffer=(char*)malloc(sizeof(myObject));  memcpy(buffer,(const unsigned char*)&myObject,sizeof(myObject));  //int result = buffer[0];  for(int i=0; i<sizeof(myObject); i++){    Serial.println(buffer[i], HEX);  }    free(buffer);    }

So what does it do?

This is the basis for creating an XOR checksum from the first part of a data structure, and then appending the checksum prior to transmission over a radio link. Could be 433MHz or nfr24l01, whatever!

Model aeroplanes, quadcopters or other remote servo based equipment can malfunction if data packets are corrupted. So the data packets are sent with a validating checksum, in this case using XOR to keep the size of the checksum down to one byte. On the receiving end, the checksum is run again to validate the checksum which if valid, can be sent to the servo and if not, a request for the data can be made by the remote to the transmitter for the data packet to be resent.

The values used in the sketch above are from a BME280 module, which produces floating-point numbers but can easily be adapted to integers for other purposes. In fact, anything you can store in an object!!

All comments welcome!


   
Quote
Pugwash
(@pugwash)
Noble Member
Joined: 4 years ago
Posts: 1012
Topic starter  

Now a demonstration of a practical implementation:

Code for remote sensor:

#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SEALEVELPRESSURE_HPA (1013.25)

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(10, 9); // CE, CSN         
const uint64_t address = 01;     //Byte of array representing the address. This is the address where we will send the data. This should be same on the receiving side.

Adafruit_BME280 bme; // Create the bme object

typedef struct{
  float temperature;
  float pressure;
  float altitude;
  float humidity;
  byte checksum;  
  }bme280data;

bme280data myObject; // declare the message object

// bool resendflag = false;

unsigned long previousTime;
unsigned long currentTime;
unsigned long delayTime = 5000; // 5 second interval

void setup() {
  
  Serial.begin(9600);
  // Print splash sheet to monitor
  Serial.print("*Source file: ");
  Serial.println(__FILENAME__); // Source file name
  Serial.print("Compile date: ");
  Serial.println(__DATE__);
  Serial.print("Compile time: ");
  Serial.println(__TIME__);
  Serial.println();
  
   //Initialise the nrf24l01
  
  radio.begin();                  //Starting the Wireless communication
  radio.openWritingPipe(address); //Setting the address where we will send the data
  radio.setPALevel(RF24_PA_MAX);  //You can set it as minimum or maximum depending on the distance between the transmitter and receiver.
  radio.startListening();         //Default in listening mode

  // Initialise bme280

  if (!bme.begin(0x76)) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
  }
  previousTime = millis();
}

void loop() {

  currentTime = millis();

  // WiP listen for resend command
  
  // if(currentTime >= previousTime + delayTime || resendflag = true){
  if(currentTime >= previousTime + delayTime){
    myObject.temperature = bme.readTemperature();
    myObject.pressure = bme.readPressure() / 100.0F;
    myObject.altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);
    myObject.humidity = bme.readHumidity();
    myObject.checksum = checkSum();
  
    radio.stopListening(); //change to transmitting mode 
    radio.write(&myObject, sizeof(myObject));
    radio.startListening(); // return to listening mode
    
    previousTime = currentTime;
    // resendflag = false; // for future use
  }
}

int checkSum(){
  
  unsigned char *buffer=(char*)malloc(sizeof(myObject));
  memcpy(buffer,(const unsigned char*)&myObject,sizeof(myObject));

  int result = buffer[0];
  for(int i=1; i<sizeof(myObject) - 1; i++){
    result ^= buffer[i];
  }
  
  free(buffer);

  return result;
  }

Code for local receiver:

 

#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(10, 9); // CE, CSN
const uint64_t address = 01;

typedef struct {
  float temperature;
  float pressure;
  float altitude;
  float humidity;
  byte checksum;  
  }bme280data;

bme280data myObject; // declare the message object

void setup() {
  
  Serial.begin(9600);
  // Print splash sheet to monitor
  Serial.print("*Source file: ");
  Serial.println(__FILENAME__); // Source file name
  Serial.print("Compile date: ");
  Serial.println(__DATE__);
  Serial.print("Compile time: ");
  Serial.println(__TIME__);
  Serial.println();

  //Initialise the nrf24l01
  
  radio.begin();
  radio.openReadingPipe(1, address);   //Setting the address at which we will receive the data
  radio.setPALevel(RF24_PA_MAX);       //You can set this as minimum or maximum depending on the distance between the transmitter and receiver.
  radio.startListening();              //This sets the module as receiver

}

void loop() {

  if (radio.available()){
    radio.read(&myObject, sizeof(myObject));
   
    Serial.println();
    Serial.print(myObject.temperature); Serial.println(" °C");
    Serial.print(myObject.pressure); Serial.println(" hPa");
    Serial.print(myObject.altitude); Serial.println(" m");
    Serial.print(myObject.humidity); Serial.println(" %");
    Serial.print("Checksum(hex): "); Serial.println(myObject.checksum, HEX);
    //Serial.println(sizeof(myObject));
    if(validateChecksum){
      Serial.println("Checksum validated");
      }else{
      Serial.println("Checksum invalid");
      }

    delay(5);
    }
}

bool validateChecksum(){

  bool valid;
  
  unsigned char *buffer=(char*)malloc(sizeof(myObject));
  memcpy(buffer,(const unsigned char*)&myObject,sizeof(myObject));

  int result = buffer[0];
  for(int i=1; i<sizeof(myObject) - 1; i++){
    result ^= buffer[i];
  }
  
  if(result == buffer[sizeof(myObject)]){
    valid = true;
    }else{
    valid = false;
    }
    
  free(buffer);

  return valid;  
}

And now a screenshot of the Serial Monitor to prove it works:

local monitor

Next steps - to implement the resend routine if the checksum is NOT validated.

 


   
frogandtoad and ZeFerby reacted
ReplyQuote
Robo Pi
(@robo-pi)
Noble Member
Joined: 4 years ago
Posts: 1909
 

@pugwash Nice work Steve.  Thanks for sharing these educational articles.  Your code is very well formatted and easily readable.  Using a struct to set up the variables is a nice touch too.

@frogandtoad, Thanks to you as well for your contributions to this project.

DroneBot Workshop Robotics Engineer
James


   
ReplyQuote
frogandtoad
(@frogandtoad)
Noble Member
Joined: 4 years ago
Posts: 1506
 

@pugwash

Glad to help, and more importantly glad that you were able to get your project up and working!
Welcome to the world of using objects to encapsulate data... once you have a grip on them, you'll never want to write procedural code again 🙂

I'll have more time over the weekend, and looking forward to your next version!

Cheers!

 


   
ReplyQuote
frogandtoad
(@frogandtoad)
Noble Member
Joined: 4 years ago
Posts: 1506
 

@robo-pi

Thank's, and you're welcome!
I hope some of OOP techniques help all viewers to see how they can be beneficial even with an Arduino!

Cheers!

 


   
ReplyQuote
Pugwash
(@pugwash)
Noble Member
Joined: 4 years ago
Posts: 1012
Topic starter  

@frogandtoad, @robo-pi 

Thank you guys, for the words of encouragement! I expect to get the next version finished by the weekend.

I hope some of OOP techniques help all viewers to see how they can be beneficial even with an Arduino!

As far as OOP is concerned, there's no turning back now! ? 

I can envisage countless possibilities for its usage. I do work with OOP in both Java and Python, but didn't realise, until now, that it could also be used with the slimmed-down version of C++ for the Arduino.

I just wonder how many other tools are lurking below the surface of C++, that can be used with the Arduino or other boards! ? 

 


   
ReplyQuote
ZeFerby
(@zeferby)
Reputable Member
Joined: 3 years ago
Posts: 381
 

@pugwash

I think I saw some templates used in libraries ("generic" classes/functions that you can "specialize" with specific datatypes).

Some C++ templates infos :

https://www.geeksforgeeks.org/templates-cpp/

http://www.cplusplus.com/doc/oldtutorial/templates/

https://en.cppreference.com/w/cpp/language/templates

Eric


   
ReplyQuote
Pugwash
(@pugwash)
Noble Member
Joined: 4 years ago
Posts: 1012
Topic starter  

@zeferby

Thanks for the tip Eric, I'll take a look at them when I have got a bit more time on my hands!

Gotta run! It's my cards and booze night ? ? ? 


   
ReplyQuote
Pugwash
(@pugwash)
Noble Member
Joined: 4 years ago
Posts: 1012
Topic starter  

 

After wallowing in the praise lavished on me yesterday, I now have to eat a large slice of humble pie!

I thought, before moving on to phase 2, I have to do one more code test and that was to force a checksum error. So I manipulated the checksum, expecting the "Checksum not valid" to be returned.

Oooops! It didn't happen!

Half an hour before my cards session, it wasn't working, I read and reread the code but still couldn't see the mistake. After a few rounds of cards and a couple of beers, it dawned on me, what I was doing wrong! So as soon as I got home and to a chorus of "what are you doing on the computer at this time of night?" (punctuated with a few expletives) from my better half, I fixed it. All I needed to do was change

if(validateChecksum){

to

if(validateChecksum()){

and it worked properly!

So this morning over a cuppa, I came up with the following alternative to "he's lost his marbles" or "he's one bun short of a baker's dozen"!

He's two parentheses short of a function call!

? ? ? ? ? 


   
ReplyQuote
Pugwash
(@pugwash)
Noble Member
Joined: 4 years ago
Posts: 1012
Topic starter  

So once the problem above was solved, another bug was discovered but a very simple one.

So here is the receiver code, fully functional, tested and in full bug free glory!

#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(10, 9); // CE, CSN
const uint64_t address_remote = 01;

typedef struct {
  float temperature;
  float pressure;
  float altitude;
  float humidity;
  byte checksum;  
  }bme280data;

bme280data myObject; // declare the message object

void setup() {
  
  Serial.begin(9600);
  // Print splash sheet to monitor
  Serial.print("*Source file: ");
  Serial.println(__FILENAME__); // Source file name
  Serial.print("Compile date: ");
  Serial.println(__DATE__);
  Serial.print("Compile time: ");
  Serial.println(__TIME__);
  Serial.println();

  //Initialise the nrf24l01
  
  radio.begin();
  radio.openReadingPipe(1, address_remote);   //Setting the address at which we will receive the data
  radio.setPALevel(RF24_PA_MAX);       //You can set this as minimum or maximum depending on the distance between the transmitter and receiver.
  radio.startListening();              //This sets the module as receiver

}

void loop() {

  if (radio.available()){
    radio.read(&myObject, sizeof(myObject));
   
    Serial.println();
    Serial.print(myObject.temperature); Serial.println(" °C");
    Serial.print(myObject.pressure); Serial.println(" hPa");
    Serial.print(myObject.altitude); Serial.println(" m");
    Serial.print(myObject.humidity); Serial.println(" %");
    Serial.print("Checksum(hex): "); Serial.println(myObject.checksum, HEX);
    //Serial.println(sizeof(myObject));
    if(validateChecksum()){
      Serial.println("Checksum validated");
      }else{
      Serial.println("Checksum invalid");
      }

    delay(5);
    }
}

bool validateChecksum(){

  bool valid = false;
  
  unsigned char *buffer=(char*)malloc(sizeof(myObject));
  memcpy(buffer,(const unsigned char*)&myObject,sizeof(myObject));

  int result = buffer[0];
  for(int i=1; i<sizeof(myObject) - 1; i++){
    result ^= buffer[i];
    }
 
  if(result == buffer[sizeof(myObject) - 1]){
    valid = true;
    }else{
    valid = false;
    }

  // Serial.println(sizeof(myObject));
  
  free(buffer);

  return valid;  
}

Watch this space!!


   
ReplyQuote
Pugwash
(@pugwash)
Noble Member
Joined: 4 years ago
Posts: 1012
Topic starter  

Gosh, that went quicker than I thought!

The code so far. If the received checksum does not match the calculated checksum, the receiver sends a request in the form of a byte with value 0xff (255 decimal) back to the remote/transmitter. When the transmitter receives 0xff it sets resendflag to "true", resends the whole object "myObject" and returns resendflag to "false".

So here is the Rev. 1 code.

For the local receiver:

#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)

#include <RF24.h>
#include <RF24Network.h>
#include <SPI.h>

// function declarations
bool validateChecksum();
void sendMyData();

//radio and network setup
RF24 radio(10,9); // nRF24L01 (CE,CSN)
RF24Network network(radio); // Include the radio in the network
const uint16_t this_node = 00; // Address of this node in Octal format ( 04,031, etc)
const uint16_t node01 = 01; // 

typedef struct {
  float temperature;
  float pressure;
  float altitude;
  float humidity;
  byte checksum;  
  }bme280data;

bme280data myObject; // declare the message object

void setup() {
  
  Serial.begin(9600);
  // Print splash sheet to monitor
  Serial.print("*Source file: ");
  Serial.println(__FILENAME__); // Source file name
  Serial.print("Compile date: ");
  Serial.println(__DATE__);
  Serial.print("Compile time: ");
  Serial.println(__TIME__);
  Serial.println();

  //Initialise the nrf24l01
  
  SPI.begin();
  radio.begin();
  network.begin(90, this_node); //(channel, node address)

}

void loop() {

  network.update();

  while (network.available()) { // Is there any incoming data?
    RF24NetworkHeader header;
    network.read(header, &myObject, sizeof(myObject)); // Read the incoming data
   
    Serial.println();
    Serial.print(myObject.temperature); Serial.println(" °C");
    Serial.print(myObject.pressure); Serial.println(" hPa");
    Serial.print(myObject.altitude); Serial.println(" m");
    Serial.print(myObject.humidity); Serial.println(" %");
    Serial.print("Checksum(hex): "); Serial.println(myObject.checksum, HEX);
    
    if(validateChecksum()){
      Serial.println("Checksum validated");
      }else{
      Serial.println("Checksum invalid - resend requested");
      sendMyData(255);
      }

    delayMicroseconds(500);
    }
}

bool validateChecksum(){

  bool valid;
  
  unsigned char *buffer=(char*)malloc(sizeof(myObject));
  memcpy(buffer,(const unsigned char*)&myObject,sizeof(myObject));

  int result = buffer[0];
  for(int i=1; i<sizeof(myObject) - 1; i++){
    result ^= buffer[i];
    // to test for invalid checksum change the above to result ^= buffer[i] + 1;
  }
  
  if(result == buffer[sizeof(myObject) - 1]){
    valid = true;
    }else{
    valid = false;
    }
    
  free(buffer);

  return valid;  
}

void sendMyData(int outgoingData){
  RF24NetworkHeader header(node01); // (Address where the data is going)
  bool ok = network.write(header, &outgoingData, sizeof(outgoingData)); // Send the data
    
 }

For the transmitter/remote sensor:

#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SEALEVELPRESSURE_HPA (1013.25)

#include <RF24.h>
#include <RF24Network.h>
#include <SPI.h>

RF24 radio(10,9); // nRF24L01 (CE,CSN)
RF24Network network(radio); // Include the radio in the network
const uint16_t this_node = 01; // Address of this node in Octal format ( 04,031, etc)
const uint16_t node00 = 00; // 

// function declarations
int checkSum();

Adafruit_BME280 bme; // Create the bme object

typedef struct{
  float temperature;
  float pressure;
  float altitude;
  float humidity;
  byte checksum;  
  }bme280data;

bme280data myObject; // declare the message object

// required true if checksum not validated
bool resendflag = false;

unsigned long previousTime;
unsigned long currentTime;
unsigned long delayTime = 5000; // 5 second interval

void setup() {
  
  Serial.begin(9600);
  // Print splash sheet to monitor
  Serial.print("*Source file: ");
  Serial.println(__FILENAME__); // Source file name
  Serial.print("Compile date: ");
  Serial.println(__DATE__);
  Serial.print("Compile time: ");
  Serial.println(__TIME__);
  Serial.println();
  
  //Initialise the nrf24l01
  
  SPI.begin();
  radio.begin();
  network.begin(90, this_node); //(channel, node address)

  // Initialise bme280

  if (!bme.begin(0x76)) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
  }
  previousTime = millis();
}

void loop() {

  network.update();

  currentTime = millis();

  // listen for resend command incoming int with a value of 255
  while (network.available()) { // Is there any incoming data?
    RF24NetworkHeader header;
    int incomingData;
    network.read(header, &incomingData, sizeof(incomingData)); // Read the incoming data
    if(incomingData == 255){
      resendflag = true;
      }
    }
  
  
  if(currentTime >= previousTime + delayTime || resendflag == true){
    myObject.temperature = bme.readTemperature();
    myObject.pressure = bme.readPressure() / 100.0F;
    myObject.altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);
    myObject.humidity = bme.readHumidity();
    myObject.checksum = checkSum();
  
    RF24NetworkHeader header(node00); // (Address where the data is going)
    bool ok = network.write(header, &myObject, sizeof(myObject)); // Send the data
        
    previousTime = currentTime;
  
    resendflag = false;
  }
}

int checkSum(){
  
  unsigned char *buffer=(char*)malloc(sizeof(myObject));
  memcpy(buffer,(const unsigned char*)&myObject,sizeof(myObject));

  int result = buffer[0];
  for(int i=1; i<sizeof(myObject) - 1; i++){
    result ^= buffer[i];
  }
  
  free(buffer);

  return result;
  }

I have gone back to using the RF24Network.h library, just in case I decide to add more sensors or repeater nodes to the network.

Now all I have to do is add a 1604 LCD display to the receiver, but I won't bore you all with that ? 

And here come the files, saves the copy and paste.

 

 


   
ReplyQuote
Pugwash
(@pugwash)
Noble Member
Joined: 4 years ago
Posts: 1012
Topic starter  

Another Quantum Leap!

The code has now been altered to accommodate one base station and two remote sensor stations. Adding more stations(nodes) should be pretty straightforward.

Resend requests are sent to the offending station directly.

I was just wondering how I could simulate this, not wishing to repeat my last mistake, when I suddenly got a very rare checksum error and as shown in the picture below, despite the remotes alternately sending data, the requested data is resent immediately from the station sending the corrupted data.

rf24 test

I will just attach the files this time, for your perusal.

All comments welcome!


   
ReplyQuote
JoeLyddon
(@joelyddon)
Reputable Member
Joined: 3 years ago
Posts: 354
 

Hi Steve,

Thank you, Thank You...  for a great Find and presentation...

I will probably change my system to use this one.

I presume that your program Links are updated after all of the changes & corrections have been made?

I don't think I would have to read all of the above and make required changes...  would I?  🙂

Thanks again!

 

Have Fun,
Joe Lyddon

www.woodworkstuff.net


   
ReplyQuote
Robo Pi
(@robo-pi)
Noble Member
Joined: 4 years ago
Posts: 1909
 

@pugwash, @frogandtoad,

I have a problem.  I'm trying to do something similar with the 433MHz radio modules.   I can send and receive strings, and I can even send an entire struct, but only if I define the contents manually.   As soon as I try to use variables it doesn't work.

One thing to note here is that I'm not  having any problem with the actual  transmitting and receiving of data.   I'm only having a problem preserving the variables within a struct.   Another thing to note here is that the following sketches include  DTH-11 sensors, Serial Monitor printing, and Displaying the output to an LED&Key board.    All those parts are working just fine.  And as I say, I'm not having any difficulty transmitting data or receiving it, save for in the case of variables when I actually receive the data but it's all the wrong numbers.

Here's the  Transmit sketch:

The problem appears to be right after the beginning of the Loop().  After reading the sensors as floats I then attempt to cast them into the struct variables as uint8_t which is the data type the RH_ASK.h library wants. 

 The program complies and runs just fine.  It even transmits the correct 5 byte size packet over to the receiver.  The problem is that the numbers received on the receiver end do not match the temperature and humidity readings at all.  Not even close.  I tried to find a pattern  but could not.

However, if I  comment out those lines and uncomment the lines below that where I just equate these to hard numbers,  it works just fine and the  receiver displays the correct numbers.

So I'm  guessing that something is going wrong when I try to cast the floats to uint8_t type.

 

// 433MHz__Transceiver_v0.ino

// ------------ RadioHead Library for Amplitude Shift Keying ------------
#include <RH_ASK.h> 
#include <SPI.h> // SPI is Required for the above library to work.
RH_ASK rf_driver; //Create the RF Driver Object. 

// Variables for Transmit and Receive
#define Radio_Data_Recv_pin 11 //Serial Data from 433Mhx Receiver
#define Radio_RELAY_pin 2 // Relay for Transmitting
#define Radio_Data_XMIT_pin 12 // Serial Data to 433Mhz Transmitter
#define radioTransmitDelay 2000 // how long to display a number
#define radioRecoveryDelay 5000 // how long between numbers.
// --- END ---- RadioHead Library for Amplitude Shift Keying ------------

// ---------------- DHT SENSOR LIBRARY and displayData -------------------
#include <DHT.h>
#define DHT_1_PIN 3     // Sensor #1
#define DHT_2_PIN 4     // Sensor #2
#define DHTTYPE DHT11   // DHT 11 
// Pin Designations and mode set?
DHT dht_1(DHT_1_PIN, DHTTYPE); // DHT library info
DHT dht_2(DHT_2_PIN, DHTTYPE); // DHT library info

// Define data structure as uint8_t:
typedef struct{
  uint8_t temp1;
  uint8_t temp2;
  uint8_t humid1;
  uint8_t humid2;
  uint8_t checksum;
} radioPacket;

// Declare the object as pacSend:  5 bytes long.
radioPacket pacSend; 

// Floats declared for the DHT Sensors
float temp10;
float temp20;
float humid10;
float humid20;

// Global variables used later in the printResults() method. 
int tempInteger;
String tempString;
// --- END -------- DHT SENSOR LIBRARY and displayData -------------------

//----------  pin numbers for the LED&KEY display board ------------
#define displayStrobe 5 // STB
#define displayClock 6 // CLK
#define displayData 7 // DIO
// Integer array for the 7-seg codes for 0 thru 9   
const int LED[] = {63,6,91,79,102,109,125,7,127,111};
//-- END ---  pin numbers for the LED&KEY display board ------------

void setup(){
  // ----- Begin File Signature ----
  Serial.begin(9600);
  Serial.print("File Location: ");
  Serial.println(__FILE__); // prints the full path
  Serial.println("NOTES:------- 433MHz__Transmitter_v0_uint8_t.ino  --------------");
  Serial.println("---------- Sends the radioPacket pacSend object  ----");
  Serial.println("");  
  // ----- END  File Signature ----

// --------- Transmit, Receive, and Relay Pin Modes --------------
pinMode(Radio_Data_XMIT_pin, OUTPUT);
pinMode(Radio_Data_Recv_pin, INPUT);
pinMode(Radio_RELAY_pin, OUTPUT);
// -- END -- Transmit, Receive, and Relaty Pin Modes --------------

// -------------------- LED & Key pins set up -------------------
pinMode(displayStrobe, OUTPUT);
pinMode(displayClock, OUTPUT);
pinMode(displayData, OUTPUT);
// --------- END ------ LED & Key pins set up -------------------

// ------------ Initiate libraries and LED display ------------
  rf_driver.init(); // Radio library
 
  Serial.println("Temperature test");
  dht_1.begin(); // Temp Sensor #1
  dht_2.begin(); // Temp Sensor #2

  // Call LED & Key methods to set up the display board
  sendCommand(0x88);  // activate and set brightness to low (change to 0x8f for full brightness)
  LED_Board_Reset();
// --- END ---- Initiate libraries and LED display ------------

} // END Setup

void loop() // Program begins:
{
  // Read DHT sensors - NOTE: These are Float variables.
  // end in 10 and 20 instead of 1 and 2   
  humid10 = dht_1.readHumidity();
  temp10 = dht_1.readTemperature();
  humid20 = dht_2.readHumidity();
  temp20 = dht_2.readTemperature();

  // Casting floats to uint8_t for ASK radio library function:
  // Note: This is the data to be transmitted but does not receive correctly.
  pacSend.humid1 = (uint8_t)humid10;
  pacSend.temp1 = (uint8_t)temp10;
  pacSend.humid2 = (uint8_t)humid20;
  pacSend.temp2 = (uint8_t)temp20;
  pacSend.checksum =  57; // make-believe checksum for now.

//// The following is commented out, 
//// but if I set these to actual numbers they are received correctly. 
//      pacSend.humid1 = 50;
//      pacSend.temp1 = 75;
//      pacSend.humid2 = 49;
//      pacSend.temp2 = 73;
//      pacSend.checksum =  57;
  
  // ------ READ First DHT sensor ----------------------
  if (isnan(temp10) || isnan(humid10)) {
    Serial.println("Failed to read from DHT");
} else {
  // Print the sensor results to the serial monitor. 
  // Note: This prints the correct data. 
  printResults(pacSend.temp1, pacSend.humid1, 1);
      // Now sending results to the LED display:
      sendCommand(0x44); // set board to single addressing.
      // Note: tempString is defined in the printResults() method. 
      // Display first digit on LED:
      displayDigit(0xC0,LED[tempString.substring(0,1).toInt()]);        
      // Display second digit on LED:
      displayDigit(0xC2,LED[tempString.substring(1,2).toInt()]);
      // Display third digit on LED:
      displayDigit(0xC4,LED[tempString.substring(2,3).toInt()]);
  } // -- END---- READ First DHT sensor ----------------------

// ------ READ Second DHT sensor ----------------------
  if (isnan(temp20) || isnan(humid20)) {
    Serial.println("Failed to read from DHT");
  } else {
      // Print the sensor results to the serial monitor. 
      // Note: This prints the correct data. 
      printResults(pacSend.temp2, pacSend.humid2, 2);  
      // Now let's send it to the display:      
      sendCommand(0x44); // set board to single addressing.
      // Note: tempString is defined in the printResults() method.
      // Display first digit on LED:
      displayDigit(0xCA,LED[tempString.substring(0,1).toInt()]);        
      // Display second digit on LED:
      displayDigit(0xCC,LED[tempString.substring(1,2).toInt()]);
      // Display third digit on LED:
      displayDigit(0xCE,LED[tempString.substring(2,3).toInt()]);
} // -- END ---- READ Second DHT sensor ----------------------

delay(5000); // 5 second delay for testing.

    // ------------ Radio Transmit  ------------
    digitalWrite(Radio_RELAY_pin, HIGH); // HIGH = Transmit Data
    delay(radioTransmitDelay); // give the transmitter time to settle
    rf_driver.send((uint8_t *)&pacSend, sizeof(pacSend)); //Send the radioPacket
    rf_driver.waitPacketSent();
    delay(50);
    digitalWrite(Radio_RELAY_pin, LOW); // Go back to Receive mode
    delay(radioRecoveryDelay);
    // --- END ---- Radio Transmit  ------------
   
} // End Loop

// The displayDigit() method displays the temperature on the LED display board.
void displayDigit(int address, int numeral){
      digitalWrite(displayStrobe, LOW);
      shiftOut(displayData, displayClock, LSBFIRST, address); // Address of digit
      shiftOut(displayData, displayClock, LSBFIRST, numeral); // Numeral 1
      digitalWrite(displayStrobe, HIGH);
}

// The printResults() method is for the Serial monitor display only.
void printResults(uint8_t tempTemp, uint8_t humidTemp, int sensorNum){
      // convert temp from celsius to Fahrenheit
    // Formula (C × 9/5) + 32 = F 
    tempTemp = ((tempTemp * 9)/5) + 32;
    char buff[25];
    sprintf(buff, "Humidity %d: ", sensorNum);
    Serial.print(buff);
    Serial.print(humidTemp);
    Serial.print(" %\t");
    sprintf(buff, "Temperature %d: ", sensorNum);
    Serial.print(buff);
    Serial.print(tempTemp);
    Serial.println(" *F");
    tempInteger = (int)tempTemp; \
    tempString = (String)tempInteger;
    if (tempString.length() < 3){
      tempString = "0" + tempString;
    }
}

// Utility methods for LED & Key display follow:
void sendCommand(uint8_t value)
{
  digitalWrite(displayStrobe, LOW);
  shiftOut(displayData, displayClock, LSBFIRST, value);
  digitalWrite(displayStrobe, HIGH);
}

// Reset method for LED & Key display board
void LED_Board_Reset()
{
  sendCommand(0x40); // set auto increment mode
  digitalWrite(displayStrobe, LOW);
  shiftOut(displayData, displayClock, LSBFIRST, 0xc0);   // set starting address to 0
  for(uint8_t i = 0; i < 16; i++)
  {
    shiftOut(displayData, displayClock, LSBFIRST, 0x00);
  }
  digitalWrite(displayStrobe, HIGH);
}

I'll post the Receiver code in the next post.

 

 

DroneBot Workshop Robotics Engineer
James


   
ReplyQuote
Robo Pi
(@robo-pi)
Noble Member
Joined: 4 years ago
Posts: 1909
 

Here's the 433MHz Receiver code:

I'm using the same struct only here I call it pacRecv instead of pacSend.

In the Radio-Receive section near the end of the Loop() I have a lot of lines commented out.   This is because I've been trying this two different ways.   However, both ways appear to work just fine when I send over  numbers that I had typed in by hand.  And they also both produced precisely the same wrong valuse when I try sending over numbers that had been cast to uint8_t from floats.

Not sure what I'm doing wrong here?  But I've been at t his for several days trying many different things and I haven't been able to send over actual variables yet. Only when I define them directly in code does it work.

// 433MHz__Receiver_v0_uint8_t.ino

// ------------ RadioHead Library for Amplitude Shift Keying ------------
#include <RH_ASK.h> 
#include <SPI.h> // SPI is Required for the above library to work.
RH_ASK rf_driver; //Create the RF Driver Object. 

// Variables for Transmit and Receive
#define Radio_Data_Recv_pin 11 //Serial Data from 433Mhx Receiver
#define Radio_RELAY_pin 2 // Relay for Transmitting
#define Radio_Data_XMIT_pin 12 // Serial Data to 433Mhz Transmitter
#define radioTransmitDelay 2000 // how long to display a number
#define radioRecoveryDelay 5000 // how long between numbers.
// --- END ---- RadioHead Library for Amplitude Shift Keying ------------

// ---------------- DHT SENSOR LIBRARY and displayData -------------------
#include <DHT.h>
#define DHT_1_PIN 3     // Sensor #1
#define DHT_2_PIN 4     // Sensor #2
#define DHTTYPE DHT11   // DHT 11 
// Pin Designations and mode set?
DHT dht_1(DHT_1_PIN, DHTTYPE); // DHT library info
DHT dht_2(DHT_2_PIN, DHTTYPE); // DHT library info

// Define data structure as as uint8_t:
typedef struct{
  uint8_t temp1;
  uint8_t temp2;
  uint8_t humid1;
  uint8_t humid2;
  uint8_t checksum;
} radioPacket;

// Declare an object as pacRecv:  5 bytes long.
radioPacket pacRecv;

// Floats declared for the DHT Sensors
float temp10;
float temp20;
float humid10;
float humid20;

// Global variables used later in the printResults() method. 
int tempInteger;
String tempString;
// --- END -------- DHT SENSOR LIBRARY and displayData -------------------

//----------  pin numbers for the LED&KEY display board ------------
#define displayStrobe 5 // STB
#define displayClock 6 // CLK
#define displayData 7 // DIO
// Integer array for the 7-seg codes for 0 thru 9   
const int LED[] = {63,6,91,79,102,109,125,7,127,111};
//-- END ---  pin numbers for the LED&KEY display board ------------

void setup(){
  // ----- Begin File Signature ----
  Serial.begin(9600);
  Serial.print("File Location: ");
  Serial.println(__FILE__); // prints the full path
  Serial.println("NOTES:------- 433MHz__Receiver_v0_uint8_t.ino  --------------");
  Serial.println("---------- Receives the radioPacket pacSend object ----");
  Serial.println("");  
  // ----- END  File Signature ----

// --------- Transmit, Receive, and Relaty Pin Modes --------------
pinMode(Radio_Data_XMIT_pin, OUTPUT);
pinMode(Radio_Data_Recv_pin, INPUT);
pinMode(Radio_RELAY_pin, OUTPUT);
// -- END -- Transmit, Receive, and Relaty Pin Modes --------------

// -------------------- LED & Key pins set up -------------------
pinMode(displayStrobe, OUTPUT);
pinMode(displayClock, OUTPUT);
pinMode(displayData, OUTPUT);
// --------- END ------ LED & Key pins set up -------------------

// ------------ Initiate libraries and LED display ------------
  rf_driver.init(); // Radio library
 
  Serial.println("Temperature test");
  dht_1.begin(); // Temp Sensor #1
  dht_2.begin(); // Temp Sensor #2

  // Call LED & Key methods to set up the display board
  sendCommand(0x88);  // activate and set brightness to low (change to 0x8f for full brightness)
  LED_Board_Reset();
// --- END ---- Initiate libraries and LED display ------------

} // END Setup

void loop() // Program begins:
{
  // Read DHT sensors - NOTE: These are Float variables.
  humid10 = dht_1.readHumidity();
  temp10 = dht_1.readTemperature();
  humid20 = dht_2.readHumidity();
  temp20 = dht_2.readTemperature();

  // ------ READ First DHT sensor ----------------------
  if (isnan(temp10) || isnan(humid10)) {
    Serial.println("Failed to read from DHT");
} else {
  // Print the sensor results to the serial monitor. 
  printResults(temp10, humid10, 1);
      // Now sending results to the LED display:
      sendCommand(0x44); // set board to single addressing.
      // Note: tempString is defined in the printResults() method.
      // Display first digit on LED:
      displayDigit(0xC0,LED[tempString.substring(0,1).toInt()]);        
      // Display second digit on LED:
      displayDigit(0xC2,LED[tempString.substring(1,2).toInt()]);
      // Display third digit on LED:
      displayDigit(0xC4,LED[tempString.substring(2,3).toInt()]);
  } // -- END---- READ First DHT sensor ----------------------

// ------ READ Second DHT sensor ----------------------
  if (isnan(temp20) || isnan(humid20)) {
    Serial.println("Failed to read from DHT");
  } else {
      // Print the sensor results to the serial monitor. 
      printResults(temp20, humid20, 2);  
      // Now let's send it to the display:
      sendCommand(0x44); // set board to single addressing.
      // Note: tempString is defined in the printResults() method.
      // Display first digit on LED:
      displayDigit(0xCA,LED[tempString.substring(0,1).toInt()]);        
      // Display second digit on LED:
      displayDigit(0xCC,LED[tempString.substring(1,2).toInt()]);
      // Display third digit on LED:
      displayDigit(0xCE,LED[tempString.substring(2,3).toInt()]);
      

} // -- END ---- READ Second DHT sensor ----------------------

delay(5000); // 5 second delay for testing.

   // ------------ Radio Receive ------------
    digitalWrite(Radio_RELAY_pin, LOW); //Recieve MODE
    // Set buffer to size of expected message
    //uint8_t buf[sizeof(pacRecv)]; // 5 Bytes
    //uint8_t buflen = sizeof(buf);     
    // Check if received packet is correct size
    //if (rf_driver.recv(buf, &buflen))
         // NOTE: I also tried the following instead of all of the above,...
         // but it didn't make any difference.
         if (rf_driver.recv((uint8_t *)&pacRecv, (uint8_t *)sizeof(pacRecv)))
    {
      //memcpy((uint8_t *) &pacRecv, buf, sizeof(pacRecv));
      // Message received
      Serial.println("Message Received: ");
      Serial.println(pacRecv.temp1);   
      Serial.println(pacRecv.humid1);   
      Serial.println(pacRecv.temp2);   
      Serial.println(pacRecv.humid2);
      Serial.println(pacRecv.checksum); // The make-believe checksum is always correct. 
    }
    delay(radioRecoveryDelay); // Probably not needed.
   // --- END ---- Radio Receive ------------ 
    
} // End Loop

// The displayDigit() method displays the temperature on the LED display board.
void displayDigit(int address, int numeral){
      digitalWrite(displayStrobe, LOW);
      shiftOut(displayData, displayClock, LSBFIRST, address); // Address of digit
      shiftOut(displayData, displayClock, LSBFIRST, numeral); // Numeral 1
      digitalWrite(displayStrobe, HIGH);
}

// The printResults() method is for the Serial monitor display only.
void printResults(uint8_t tempTemp, uint8_t humidTemp, int sensorNum){
      // convert temp from celsius to Fahrenheit
    // Formula (C × 9/5) + 32 = F 
    tempTemp = ((tempTemp * 9)/5) + 32;
    char buff[25];
    sprintf(buff, "Humidity %d: ", sensorNum);
    Serial.print(buff);
    Serial.print(humidTemp);
    Serial.print(" %\t");
    sprintf(buff, "Temperature %d: ", sensorNum);
    Serial.print(buff);
    Serial.print(tempTemp);
    Serial.println(" *F");
    tempInteger = (int)tempTemp; \
    tempString = (String)tempInteger;
    if (tempString.length() < 3){
      tempString = "0" + tempString;
    }
}

// Utility methods for LED & Key display follow:
void sendCommand(uint8_t value)
{
  digitalWrite(displayStrobe, LOW);
  shiftOut(displayData, displayClock, LSBFIRST, value);
  digitalWrite(displayStrobe, HIGH);
}

// Reset method for LED & Key display board
void LED_Board_Reset()
{
  sendCommand(0x40); // set auto increment mode
  digitalWrite(displayStrobe, LOW);
  shiftOut(displayData, displayClock, LSBFIRST, 0xc0);   // set starting address to 0
  for(uint8_t i = 0; i < 16; i++)
  {
    shiftOut(displayData, displayClock, LSBFIRST, 0x00);
  }
  digitalWrite(displayStrobe, HIGH);
}

 

 

DroneBot Workshop Robotics Engineer
James


   
ReplyQuote
Page 1 / 7