Sending GPS readings using RadioHead for LoRa Dragino Module

Hi, I'm trying to make 2 Arduinos communicate over RF 915MHz using the RadioHead library. One of the Arduinos will take some sensor readings, right now it's only distance but I plan to add more data later. What I want to do is pass this variable to the send-function and transmit them both at the same time. So I want all the readings to be sent in the same transmission and I will then have to split them up on the receiving side.

The serial monitor outputs the print lines in loop but does not give actual distance values. I implemented

My sketch now looks like this (far from being finished but I want to solve this problem first). The code files are provided below. Base is where I want to receive the data. The Payload is the data I want transferred to base. The GPS is connected to Serial1. Both Dragino shields are connected onto Arduino Mega. I have the Dragino GPS/LoRa as Payload and Dragino LoRa as Base. The GPS baudrate was tested to be 9600 using the NEOGPS library diagnostic sample. NeoGPS and RadioHead (RF95Reliable sample code) are the libraries being used.

Appreciate any input on how to work with classes, variables, data storage. I am very new to this C++ programming so let me know if you need any other info to help me out. Thank you.

BASEdistance.ino (3.02 KB)

PAYLOADdistance.ino (3.76 KB)

If its working what is the question ?

It does not work... The print lines are printed. The data (distance) isn't being sent. I specified this.

do the basic Rx and Tx example programs using Radiohead work OK?

You can't use delay like that. While the Arduino is stuck at the delay statement, GPS data continues to arrive. It will eventually overflow the input buffer, and you will not get any fixes.

The NeoGPS examples show the correct program structure. Your task, SendValues, is always dependent on new GPS data, so your sketch must constantly check gps.available() until it is true. This is just like waiting for Serial.available() before calling Serial.read() to get one character.

I would suggest a loop structure like this:

// rf95_payload client
// Sends data to the Base
// LoRa Simple Server for Arduino :
// Support Devices: LoRa Shield/GPS + Arduino
#include <GPSport.h>
#include <NMEAGPS.h>
NMEAGPS gps;
NeoGPS::Location_t base( -253448688L, 1310324914L ); // Ayers Rock, AU

#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>

#define CLIENT_ADDRESS 1
#define SERVER_ADDRESS 2

// Singleton instance of the radio driver
RH_RF95 rf95;

// Class to manage message delivery and receipt
RHReliableDatagram manager(rf95, CLIENT_ADDRESS);

float frequency = 915.0; // Change the frequency here.

struct dataStruct {
  float         dist;
  unsigned long counter;
} SensorReadings;

// RF communication, Dont put this on the stack:
byte buf[sizeof(SensorReadings)] = {0};

void setup()
{
  DEBUG_PORT.begin(115200);

  DEBUG_PORT.println( F("LoRa Distance Payload") ); // F macro saves RAM
  if (!manager.init())
    DEBUG_PORT.println("LoRa init failed");

  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
  rf95.setFrequency(frequency);

  rf95.setTxPower(13, false);

  rf95.setCADTimeout(10000);
  DEBUG_PORT.println( F("Waiting for radio to setup") );
  delay(1000);
  DEBUG_PORT.println( F("Setup completed") );
  delay(1000);
  DEBUG_PORT.println( F("Looking for GPS device on " GPS_PORT_NAME) );
  gpsPort.begin(9600);

//  SensorReadings.dist = 0;
  SensorReadings.counter = 0;


} // Setup


void loop()
{
  // Check for GPS characters to parse.  A new fix may become available.
  if (gps.available( gpsPort )) {
    //  A new fix structure is ready.  This happens once per second.

    gps_fix fix = gps.read(); // save the latest

    // When we have a location, calculate how far away we are from the base location.
    if (fix.valid.location) {
      float range = fix.location.DistanceMiles( base );

      DEBUG_PORT.print( F("Range: ") );
      DEBUG_PORT.print( range );
      DEBUG_PORT.println( F(" Miles") );
      SensorReadings.dist = range;
      SendValues();
    }
    else
    {
      DEBUG_PORT.println( F("No location.") );
    }
  }


//  // Send a message to manager_server
//  if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS))
//  {
//    // Now wait for a reply from the server
//    uint8_t len = sizeof(buf);
//    uint8_t from;
//    if (manager.recvfromAckTimeout(buf, &len, 2000, &from))
//    {
//      DEBUG_PORT.print("got reply from : 0x");
//      DEBUG_PORT.print(from, HEX);
//      DEBUG_PORT.print(": ");
//      DEBUG_PORT.println((char*)buf);
//      DEBUG_PORT.print("RSSI: ");
//      DEBUG_PORT.println(rf95.lastRssi(), DEC);
//    }
//    else
//    {
//      DEBUG_PORT.println("No reply, is base running?");
//    }
//  }
//  else
//    DEBUG_PORT.println("sendtoWait failed - this is payload");
//  delay(500);

} //  loop


//RF communication
void SendValues()
{
  //Load message into data-array

  byte zize=sizeof(SensorReadings);
  memcpy (buf, &SensorReadings, zize);

  DEBUG_PORT.println( F("Sending to Base") );

  // Send a message to manager_server
  if (manager.sendto(buf, zize, SERVER_ADDRESS))
  {
    DEBUG_PORT.println( F("Message sent") );
  }
  else
  {
    DEBUG_PORT.println( F("sendto failed") );
  }

  SensorReadings.counter = SensorReadings.counter + 1;
}

This is not good coding practice:

      distance(); //Read temp sensor

There is no temperature sensor, so this is very confusing.

horace:
do the basic Rx and Tx example programs using Radiohead work OK?
RFM9X Test | Adafruit RFM69HCW and RFM9X LoRa Packet Radio Breakouts | Adafruit Learning System

I have used code from this library that worked. Arduino-Profile-Examples/libraries/Dragino at master · dragino/Arduino-Profile-Examples · GitHub
The default RadioHead samples for RF95 did not work.
The two codes were in the Arduino-Profile-Examples/libraries/Dragino/examples/LoRa/LoRa_Simple_Client_Arduino/
and
Arduino-Profile-Examples/libraries/Dragino/examples/LoRa/LoRa_Simple_Server_Arduino/
directories.

-dev:
You can't use delay like that. While the Arduino is stuck at the delay statement, GPS data continues to arrive. It will eventually overflow the input buffer, and you will not get any fixes.

The NeoGPS examples show the correct program structure. Your task, SendValues, is always dependent on new GPS data, so your sketch must constantly check gps.available() until it is true. This is just like waiting for Serial.available() before calling Serial.read() to get one character.

I would suggest a loop structure like this:

// rf95_payload client

// Sends data to the Base
// LoRa Simple Server for Arduino :
// Support Devices: LoRa Shield/GPS + Arduino
#include <GPSport.h>
#include <NMEAGPS.h>
NMEAGPS gps;
NeoGPS::Location_t base( -253448688L, 1310324914L ); // Ayers Rock, AU

#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>

#define CLIENT_ADDRESS 1
#define SERVER_ADDRESS 2

// Singleton instance of the radio driver
RH_RF95 rf95;

// Class to manage message delivery and receipt
RHReliableDatagram manager(rf95, CLIENT_ADDRESS);

float frequency = 915.0; // Change the frequency here.

struct dataStruct {
  float        dist;
  unsigned long counter;
} SensorReadings;

// RF communication, Dont put this on the stack:
byte buf[sizeof(SensorReadings)] = {0};

void setup()
{
  DEBUG_PORT.begin(115200);

DEBUG_PORT.println( F("LoRa Distance Payload") ); // F macro saves RAM
  if (!manager.init())
    DEBUG_PORT.println("LoRa init failed");

// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
  rf95.setFrequency(frequency);

rf95.setTxPower(13, false);

rf95.setCADTimeout(10000);
  DEBUG_PORT.println( F("Waiting for radio to setup") );
  delay(1000);
  DEBUG_PORT.println( F("Setup completed") );
  delay(1000);
  DEBUG_PORT.println( F("Looking for GPS device on " GPS_PORT_NAME) );
  gpsPort.begin(9600);

//  SensorReadings.dist = 0;
  SensorReadings.counter = 0;

} // Setup

void loop()
{
  // Check for GPS characters to parse.  A new fix may become available.
  if (gps.available( gpsPort )) {
    //  A new fix structure is ready.  This happens once per second.

gps_fix fix = gps.read(); // save the latest

// When we have a location, calculate how far away we are from the base location.
    if (fix.valid.location) {
      float range = fix.location.DistanceMiles( base );

DEBUG_PORT.print( F("Range: ") );
      DEBUG_PORT.print( range );
      DEBUG_PORT.println( F(" Miles") );
      SensorReadings.dist = range;
      SendValues();
    }
    else
    {
      DEBUG_PORT.println( F("No location.") );
    }
  }

//  // Send a message to manager_server
//  if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS))
//  {
//    // Now wait for a reply from the server
//    uint8_t len = sizeof(buf);
//    uint8_t from;
//    if (manager.recvfromAckTimeout(buf, &len, 2000, &from))
//    {
//      DEBUG_PORT.print("got reply from : 0x");
//      DEBUG_PORT.print(from, HEX);
//      DEBUG_PORT.print(": ");
//      DEBUG_PORT.println((char*)buf);
//      DEBUG_PORT.print("RSSI: ");
//      DEBUG_PORT.println(rf95.lastRssi(), DEC);
//    }
//    else
//    {
//      DEBUG_PORT.println("No reply, is base running?");
//    }
//  }
//  else
//    DEBUG_PORT.println("sendtoWait failed - this is payload");
//  delay(500);

} //  loop

//RF communication
void SendValues()
{
  //Load message into data-array

byte zize=sizeof(SensorReadings);
  memcpy (buf, &SensorReadings, zize);

DEBUG_PORT.println( F("Sending to Base") );

// Send a message to manager_server
  if (manager.sendto(buf, zize, SERVER_ADDRESS))
  {
    DEBUG_PORT.println( F("Message sent") );
  }
  else
  {
    DEBUG_PORT.println( F("sendto failed") );
  }

SensorReadings.counter = SensorReadings.counter + 1;
}



This is not good coding practice:



distance(); //Read temp sensor



There is no temperature sensor, so this is very confusing.

I apologize for the confusing comments. I have copy pasted from several files trying to learn. Thank you helping out with delay information. I will take some time to look at the code you provided.

Well watever you do, first reduce the programs, one just to test the GPS and another just to test the LoRa side sending dummy data.

gurenmarkv:
The default RadioHead samples for RF95 did not work.

I just ran the Radiohead Tx example on a Feather 32u4 Lora and the Rx on a Dragino Lora shield and communication worked OK
tried Tx on Dragino Lora shield and Rx on the 32u4 and that also worked OK
on the Dragino Lora shield did you set up the pin out to

/* for shield */
#define RFM95_CS 10
#define RFM95_RST 9
#define RFM95_INT 2

also check the pin out for the Dragino GPS/LoRa

I have a Dragino GPS/LoRa shield somewhere will see if I can find it - not found must have loaned it out!

have an application using a Mega where I receive serial data from a sensor and transmit it to a Lora gateway and onto The Things network and a database using HTTP Integration
the basic flow looks like

void loop(){
  if (Serial1.available()) {
    int inByte = Serial1.read();
    if(inByte != '\n')
       //  add character to data array
      ....
   else
     {
      // end of line, convert character array to integer array 
     ...
     // transmit integer array as 16bit binary values to Lora network
    ...
}

clearly there is code to receive data from the Lora network etc

horace:
I just ran the Radiohead Tx example on a Feather 32u4 Lora and the Rx on a Dragino Lora shield and communication worked OK
tried Tx on Dragino Lora shield and Rx on the 32u4 and that also worked OK
on the Dragino Lora shield did you set up the pin out to

/* for shield */

#define RFM95_CS 10
#define RFM95_RST 9
#define RFM95_INT 2



also check the pin out for the Dragino GPS/LoRa

I do not understand what defining the pinout would do. I thought the shield would just work on Mega. I can transfer a string using the code provided on the Dragino GitHub page, just not the default RF95 samples. I am very confused on what is data, and what purpose buf and len have in transferring data through RadioHead. I would appreciate help on that. For RF95 what is the simplest code to transfer a float value from transmitter to receiver?

the constants define the pins used for the RF95 Chip Select, Reset and interrupt signals and have to set up the board being used, e.g. for the adafruit 32u4 Lora I used

/* for feather32u4 */
#define RFM95_CS 8
#define RFM95_RST 4
#define RFM95_INT 7

for the Dragino Lora shield I used

/* for shield */
#define RFM95_CS 10
#define RFM95_RST 9
#define RFM95_INT 2

in the rf95.send() method

 bool    send(const uint8_t* data, uint8_t len);

data is a pointer (the address of) to the start of data in memory and len is the number of bytes to transmit
to send a float it would look something like

      float data1=3.14159f;
      rf95.send((uint8_t*)&data1, sizeof(data1));

the adress of the float variable data1 is cast to a uint8_t* in the function call

Might as well set it up to transfer a 'struct' since you plan on sending data from multiple sensors.

to send a struct it would look like

struct dataStruct{
  float dist;
  unsigned long counter;
}SensorReadings;
...
      rf95.send((uint8_t*)&SensorReadings, sizeof(SensorReadings));

cast the address of SensorReadings to a (uint8_t*)

Basically went on a 3 week break from programming. Came back. I tried to send some temperature data using the code provided by This Amazing Post and realized I needed to change to RF95 version. So using the RF95 Reliable Datagram example I was able to come up with this. The RF95 reliable datagram seems to work. But for this I cannot seem to understand how to send values from data structure or how to array it properly.

It compiles but I do not see any output on RX side, the serial output is blank. And TX side it displays the temperature only once, then it just prints the first print line from SendValues.

Transmitting TX Code

#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>
#include <OneWire.h>
#include <DallasTemperature.h>

//RH communication
#define CLIENT_ADDRESS 1
#define SERVER_ADDRESS 2
// Singleton instance of the radio driver
RH_RF95 driver;
// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram manager(driver, CLIENT_ADDRESS);

//OneWire sensors
// Data wire is plugged into port 16 on the Arduino
#define ONE_WIRE_BUS 10
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

struct dataStruct{
  float tempValue;
  unsigned long counter;
   
}SensorReadings;

 // RF communication, Dont put this on the stack:
  byte buf[sizeof(SensorReadings)] = {0};

void setup()
{
  Serial.begin(9600);
 
  //RF communication
  if (!manager.init())
    Serial.println("init failed");

  // OneWire sensors
  sensors.begin();

  SensorReadings.tempValue = 0;
  SensorReadings.counter = 0;
}

void loop()
{
  Serial.println("------------------------------------");
  float tempValue;
  int soilValue;
 
  temperature(); //Read temp sensor
  delay(1000);

  SendValues(); //Send sensor values
  delay(2000);
  Serial.println("------------------------------------");
}

//Get temperatures from Dallas sensor
void temperature()
{
  // issue a global temperature request to all devices on the bus
  Serial.print("Requesting temperatures from all...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  Serial.println("DONE");
  // Print temperatures
  Serial.print("Temperature for the device 1 (index 0) is: ");
  Serial.println(sensors.getTempCByIndex(0));
  Serial.println(""); 
  SensorReadings.tempValue = sensors.getTempCByIndex(0);
}



//RF communication
void SendValues()
{ 
  Serial.println("Sending to rf95_reliable_datagram_server");
    //uint8_t data[] = {SensorReadings};
    // Dont put this on the stack:
    uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];

  // Send a message to manager_server
  if (manager.sendtoWait((uint8_t*)&SensorReadings, sizeof(SensorReadings), SERVER_ADDRESS))
  {
    // Now wait for a reply from the server
    uint8_t len = sizeof(buf);
    uint8_t from;   
    if (manager.recvfromAckTimeout(buf, &len, 2000, &from))
    {
      Serial.print("got reply from : 0x");
      Serial.print(from, HEX);
      Serial.print(": ");
      Serial.println((char*)buf);
    }
    else
    {
      Serial.println("No reply, is rf95_reliable_datagram_server running?");
    }
  }
  else{
    Serial.println("sendtoWait failed");
  }

  delay(500);
  SensorReadings.counter = SensorReadings.counter + 1;
}

Receiving RX Code

#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>

#define CLIENT_ADDRESS 1
#define SERVER_ADDRESS 2

// Singleton instance of the radio driver
RH_RF95 driver;
// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram manager(driver, SERVER_ADDRESS);

struct dataStruct{
  float tempValue;  //Store temperature sensor value (degC)
  unsigned long counter;
   
}SensorReadings;

void setup()
{
  Serial.begin(9600);
  if (!manager.init())
    Serial.println("init failed");

  SensorReadings.tempValue = 0;
  SensorReadings.counter = 0;
}

void loop()
{
ReciveFromSensors();
}

void ReciveFromSensors()
{
  // Dont put this on the stack:
  uint8_t buf[sizeof(SensorReadings)];
  uint8_t from;
  uint8_t len = sizeof(buf);
 
  if (manager.available())
  {   
    // Wait for a message addressed to us from the client
    if (manager.recvfromAck(buf, &len, &from))
    {
      int i;
      memcpy(&SensorReadings, buf, sizeof(SensorReadings));
      Serial.println("--------------------------------------------");
      Serial.print("Got message from unit: ");
      Serial.println(from, DEC);
      Serial.print("Transmission number: ");
      Serial.println(SensorReadings.counter);
      Serial.println("");
       
      Serial.print("Temperature: ");
      Serial.println(SensorReadings.tempValue);
      Serial.println("--------------------------------------------");
    }
  }
}

Would greatly appreciate some advice like before. Thank you.

are you sure that the Chip Select etc correct
I am using a couple of 32u4 Lora boards and had to set up RFM95_CS etc, e.g.

// Server 

#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>

#define CLIENT_ADDRESS 1
#define SERVER_ADDRESS 2

/* for feather32u4 */
#define RFM95_CS 8
#define RFM95_RST 4
#define RFM95_INT 7

// Singleton instance of the radio driver
RH_RF95 driver(RFM95_CS, RFM95_INT);
// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram manager(driver, SERVER_ADDRESS);

struct dataStruct{
  float tempValue;  //Store temperature sensor value (degC)
  unsigned long counter;
   
}SensorReadings;

void setup()
{
  while(!Serial);
  delay(1000);
  Serial.begin(9600);
  Serial.println("receiver start");
  if (!manager.init())
    Serial.println("init failed");
  Serial.println("receiver OK");
  SensorReadings.tempValue = 0;
  SensorReadings.counter = 0;
}

the server displays

receiver
receiver OK
--------------------------------------------
Got message from unit: 1
Transmission number: 0

Temperature: -127.00
--------------------------------------------
--------------------------------------------
Got message from unit: 1
Transmission number: 1

Temperature: -127.00
--------------------------------------------

and the client displays

Requesting temperatures from all...DONE
Temperature for the device 1 (index 0) is: -127.00

Sending to rf95_reliable_datagram_server
sendtoWait failed
------------------------------------
------------------------------------
Requesting temperatures from all...DONE
Temperature for the device 1 (index 0) is: -127.00

Sending to rf95_reliable_datagram_server
sendtoWait failed

we are getting transmission from client to server OK
not sure why the sendtoWait failed message is displayed - will have to look into that