Xenon standalone with LoRa

I am trying to use my xenon standalone, without the mesh network, with a Adafruit FeatherWing RFM95 LoRa 900 MHz attached to it. The goal is to send sensor data from my xenon + LoRa to a Argon/Boron. My problem is that in loop(), the RadioHead .send() command will execute once okay, and then on the second execution in a loop it never returns from the .send() command. The .send() randomly executes between one and five seconds, controlled by a millis() based timer. I am flashing the code to the xenon by a cloud compile first, and then uploading to the xenon via CLI and usb.

The same code executes perfectly on a Argon connected to WiFi & the particle cloud, with the only difference is the command SYSTEM_MODE(MANUAL); is not executed on the Argon. If I run code on the xenon as LoRa “receiver”, it works fine.

I know that the LoRa RadioHead library command .send() is sensitive to interrupts, but I don’t know what could be in conflict. I have tried using different digital pins for the RadioHead library, but no change in the behavior.

I also have a Adafruit M0 with LoRa radio (built-in), plus a M0 and a separate FeatherWing LoRa radio, and the code works fine on these devices.

You can see the xenon LoRa “sender” code here: http://www.savvysolutions.info/savvymicrocontrollersolutions/particle.php?topic=particle-xenon#XenonLoRa

1 Like

Have you tried it with SYSTEM_THREAD(ENABLED); ?
https://docs.particle.io/reference/device-os/firmware/xenon/#system-thread

Yes, I tried adding SYSTEM_THREAD(ENABLED); No change, the loop() gets stuck on the second execution of the LoRa .send() command. I also tried wrapping the LoRa .send() command with SINGLE_THREADED_BLOCK() and ATOMIC_BLOCK() with no success.

I also tried adding the .send() within a for loop in the setup(), to see if the issue was particular to the loop(). I experienced the same behavior. Not sure if that provides any useful information.

Note that when I run the same code on my Argon (connected to the Particle cloud), it runs fine for many hours. I have no doubt that if I connected my Xenon to the Particle cloud via the mesh network, that the problem would go away.

Yes, I tried adding SYSTEM_THREAD(ENABLED); No change, the loop() gets stuck on the second execution of the LoRa .send() command. I also tried wrapping the LoRa .send() command with SINGLE_THREADED_BLOCK() and ATOMIC_BLOCK() with no success.

I also tried adding the .send() within a for loop in the setup(), to see if the issue was particular to the loop(). I experienced the same behavior. Not sure if that provides any useful information.

Note that when I run the same code on my Argon (connected to the Particle cloud), it runs fine for many hours. I have no doubt that if I connected my Xenon to the Particle cloud via the mesh network, that the problem would go away.

@rickkas7 any ideas?
I can’t test this :confused:

@markwkiehl, I have this working with a Xenon and Adafruit 900mHz board. I am using both the RF95 driver and RHreliabledatagram manager. It runs SYSTEM_THREAD(ENABLED) and SYSTEM_MODE(MANUAL). Not sure it matters but in setup() I run Mesh.disconnect() and Mesh.off(). My sends in loop() are manager.sendtowait(data, sizeof(data), SERVER_ADDRESS). The system is on a lipo and has been in service sending me data daily for 8 months. I did have many issues with RH RF_95 libs before I isolated one that would work reliably. I am stranded at a different site than my code but I may be able to locate the libs. As I remember they were from rickkas7. Once you get it configured it is a great radio.

1 Like

norstar:

Thank you for taking the time to offer suggestions. I added mesh.disconnect() and mesh.off() but that did not change anything. Then I switched to using the RHreliabledatagram manager. Now the behavior is different - my Xenon gets stuck on the manager.sendtowait(data, sizeof(data), SERVER_ADDRESS) and never returns (not even once). Recall that prior to using RHreliabledatagram manager I was able to execute one send and then I would get stuck on the second send. The same code minus the mesh particular code on a Adafruit Mo works just fine. And I can run the code on an Argon without issue.

#include "Particle.h"
// Command below will prevent the device connecting to the mesh network.
// NOTE:  In SYSTEM_MODE(MANUAL), the status LED with breathe white. This is normal.
SYSTEM_MODE(MANUAL);
// Insure the application loop is not interrupted by the system background processing and network management.
SYSTEM_THREAD(ENABLED);


const byte pinBuiltInLED = D7;
//////////////////////////////////////////////////////////////////////////////
// 10000 ms = 10 sec = 0.1 Hz 
// 1000 ms = 1 sec = 1 Hz
// 100 ms = 0.1 sec = 10 Hz
// 10 ms = 0.01 sec = 100 Hz
unsigned long timerInterval = random(1000,5000);  
unsigned long timerLast = 0;  // timer
//////////////////////////////////////////////////////////////////////////////
//  LoRa 900 MHz Radio
//  RadioHead library for LoRa Radio
//  install library: RF9X-RK
//  https://github.com/rickkas7/RF9X-RK
#include <RHReliableDatagram.h>
#define SENDER_ADDRESS 1
#define RECEIVER_ADDRESS 2
#include <RH_RF95.h>
// Argon/Boron/Xenon: A=D6, B=D5, C=D4, D=D3, E=D2, F=D14
#define RFM95_INT D2  // Ds="E"
#define RFM95_CS  D6  // D6="A"
#define RFM95_RST D14 // D14="F"

// Define frequency (set later)
#define RF95_FREQ 915.0
// Singleton instance of the radio driver
RH_RF95 rf95(RFM95_CS, RFM95_INT);
// Class to manage message delivery and receipt, using the rf95 declared above
RHReliableDatagram manager(rf95, SENDER_ADDRESS);
//////////////////////////////////////////////////////////////////////////////
byte cData[31]; // 16+1+13+1=31 "20200216T150644Z;++6.534755E+06"
byte buf[RH_RF95_MAX_MESSAGE_LEN]; // holds reply from receiver
unsigned long successCount = 0;
boolean bStopOnError = false;
//////////////////////////////////////////////////////////////////////////////
// AF 128x32 mono OLED   
// The 128x32 monochrome OLED uses I2C (D0 & D1; 0x3C) D2,D3,D4
// https://github.com/rickkas7/oled-wing-adafruit
// Install Library: oled-wing-adafruit
#include "oled-wing-adafruit.h"
OledWingAdafruit display;
//////////////////////////////////////////////////////////////////////////////


void setup() {
  pinMode(pinBuiltInLED, OUTPUT);

  // Make sure the Xenon is not connected to the Mesh network
  Mesh.disconnect();
  Mesh.off(); 

   /* START AF FeatherWing OLED 128x32 mono */
  display.setup();
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextWrap(false);
  display.setTextSize(1);   
  display.setCursor(0,0);
  display.print("Xenon + LoRa");
  display.display();

  pinMode(RFM95_RST, OUTPUT);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);
  // manual reset  (pull low for 100 us)
  digitalWrite(RFM95_RST, LOW);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);

  while (!manager.init()) {
    while (1) blinkERR(pinBuiltInLED);
  }
  // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
  if (!rf95.setFrequency(RF95_FREQ)) {
    while (1) blinkERR(pinBuiltInLED);
  }
  // The default transmitter power is 13dBm, using PA_BOOST.
  // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then 
  // you can set transmitter powers from 5 to 23 dBm:
  rf95.setTxPower(23, false);
  //////////////////////////////////////////////////////////////////////////////

} // setup()


// loop() runs over and over again, as quickly as it can execute.
void loop() {
  // Send a message at a random interval between 500 and 5000 ms
  //if (timerLast > millis())  timerLast = millis();
  if ((millis() - timerLast) > timerInterval) {
    blinkLED(pinBuiltInLED);
    digitalWrite(pinBuiltInLED, HIGH);

    // Create a message to send with random content.
    // "20200216T150644Z;++6.534755E+06"
    BuildSimulatedSerialSensorData(cData, sizeof(cData));
    
    display.clearDisplay();
    display.setTextSize(2);
    display.setCursor(0,0);
    display.print("Msg send");
    display.setCursor(0,16);
    successCount++;
    display.print(successCount);
    display.display();

    if (manager.sendtoWait(cData, sizeof(cData), RECEIVER_ADDRESS)) {
      display.clearDisplay();
      display.setTextSize(2);
      display.setCursor(0,0);
      display.print("Msg sent..");
      display.setCursor(0,16);
      display.print(successCount);
      display.display();
      // Now wait for a reply from the server
      uint8_t len = sizeof(buf);
      uint8_t from;   
      if (manager.recvfromAckTimeout(buf, &len, 2000, &from)) {
        digitalWrite(pinBuiltInLED, LOW); 
        // Compare the received data buff to the data sent arrToEncrypt
        int matches = 0;
        for(int i=0; i < sizeof(cData); i++){
          if(buf[i]==cData[i]){     
            matches++;  
          } 
        }
        if (matches == sizeof(cData)) {
          successCount++;
          digitalWrite(pinBuiltInLED, LOW); 
        } else {
          // The receiver didn't send back the same message content as
          // what was sent by this sender. 
          if (bStopOnError == true) 
            while (1) blinkERR(pinBuiltInLED);
        }
      }
    } else {
      display.clearDisplay();
      display.setTextSize(2);
      display.setCursor(0,0);
      display.print("SEND ERR");
      display.setCursor(0,16);
      display.print(successCount);
      display.display();
    }

I think you need to put timerLast = millis(); back in

if ((millis() - timerLast) > timerInterval) {
timerLast = millis();

Found some of my code on onedrive. I am using the rf9x-rk lib from rickkas7. While it may not be an issue I used;

  • D2 to G0 // INT
  • D3 to EN // RST
  • A5 to CS // SPI chip select

I also remember setting many debug prints in the libs to help me work through where I had issues. In RH_RF95.cpp set a few debug prints in send() to locate where its hanging since RHdatagram sendto() calls send(). Not that you need it to resolve this issue but a SDR (like RTL-SDR) will be a big help once your node is working and you start testing client/node to server/gateway communication. If you have an SPI logic tester (I use an old Saleae Logic) now is the time to hook it up.

I just noticed the pinouts in my post above are from a different LoRa project where I used a LoRa breakout board not a featherwing, please ignore.

Set a debug print before and after setModeTx() in RH_RF95::send() and see if the interrupt fires.

@markwkiehl :This code using an adafruit rf95 feather with a xenon on OS 1.4.4 works for me

#include <Particle.h>
#include <RF9X-RK.h>

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

#define CLIENT_ADDRESS 1
#define SERVER_ADDRESS 2

/*
Adafruit rf95 feather wiring:
CS->A
RST->F
IRQ->E
*/
#define RFM95_CS D3
#define RFM95_RST A5
#define RFM95_INT D2 
#define RF95_FREQ 915.0

SYSTEM_MODE(MANUAL); 
SYSTEM_THREAD(ENABLED);
// Singleton instance of the radio driver
RH_RF95 driver(D6, D2);

float frequency = 915.0;
char radiopacket[100];

// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram manager(driver, CLIENT_ADDRESS);

uint32_t timer = millis();
char data[20];
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
int logRate = 5000;
byte sendLen;
 
void setup() {

    Mesh.disconnect();
    Mesh.off();  
    Particle.disconnect();  

    pinMode(RFM95_RST, OUTPUT);  
    digitalWrite(RFM95_RST, HIGH);
    
    Serial.begin(115200) ; 

    // manual reset
    digitalWrite(RFM95_RST, LOW);
    delay(10);
    digitalWrite(RFM95_RST, HIGH);
    delay(10);

	if (!manager.init())
		Serial.println("init failed");

	driver.setFrequency(frequency);

	driver.setTxPower(23, false);

}//setup


void loop()
{

    if (millis() - timer > logRate) {
        timer = millis(); // reset the timer
    
        snprintf(radiopacket, sizeof(radiopacket),"aFloat:,%1.2f",9.9); //you'd initialize data to send here

        sendLen = strlen(radiopacket);
        radiopacket[sendLen] = '\0';

        Serial.println("Sending to rf95_reliable_datagram_server");

        // Send a message to manager_server
        if (manager.sendtoWait((uint8_t*)radiopacket, sizeof(radiopacket), 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))
    		{
    			buf[len] = 0;
			    Serial.printlnf("got reply from 0x%02x rssi=%d %s", from, driver.lastRssi(), (char *) buf);
		    }
    		else
		    {
    			Serial.println("No reply, is rf95_reliable_datagram_server running?");
    		}
    	}
    	else
		    Serial.println("sendtoWait failed");
	    delay(500);
    }//if (millis() - timer > 2000) 
}//loop

Thank you for taking the time to help. I tried it, but no success. What is your library source?

RF9X-RK which is available on the WEB IDE (https://build.particle.io/) as well as on Particle Workbench

I am using the library from RF9X-RK. The following is a more simple test that still never returns from the manager.sendtoWait()

/*
 * Project      Xenon + LoRa sender
 * Description: Xenon with LoRa RFM95W 900 MHz radio sends date/time stamped
 *              unencrypted message to a receiving Xenon / Argon / Boron 
 *              via LoRa. 
 * Author:      Mark Kiehl / Mechatronic Solutions LLC
 * Date:        March 2020
 */

#include <Particle.h>
// Command below will prevent the device connecting to the mesh network.
// NOTE:  In SYSTEM_MODE(MANUAL), the status LED with breathe white. This is normal.
SYSTEM_MODE(MANUAL);
// Insure the application loop is not interrupted by the system background processing and network management.
SYSTEM_THREAD(ENABLED);


const byte pinBuiltInLED = D7;
//////////////////////////////////////////////////////////////////////////////
// 10000 ms = 10 sec = 0.1 Hz 
// 1000 ms = 1 sec = 1 Hz
// 100 ms = 0.1 sec = 10 Hz
// 10 ms = 0.01 sec = 100 Hz
unsigned long timerInterval = random(1000,5000);  
unsigned long timerLast = 0;  // timer
//////////////////////////////////////////////////////////////////////////////
//  LoRa 900 MHz Radio
//  RadioHead library for LoRa Radio
//  install library: RF9X-RK
//  https://github.com/rickkas7/RF9X-RK
#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>  
#define SENDER_ADDRESS 1
#define RECEIVER_ADDRESS 2
// Argon/Boron/Xenon: A=D6, B=D5, C=D4, D=D3, E=D2, F=D14
#define RFM95_INT D2  // Ds="E"
#define RFM95_CS  D6  // D6="A"
#define RFM95_RST D14 // D14="F"

// Define frequency (set later)
#define RF95_FREQ 915.0
// Singleton instance of the radio driver
RH_RF95 rf95(RFM95_CS, RFM95_INT);
// Class to manage message delivery and receipt, using the rf95 declared above
RHReliableDatagram manager(rf95, SENDER_ADDRESS);
//////////////////////////////////////////////////////////////////////////////
byte buf[RH_RF95_MAX_MESSAGE_LEN]; // holds reply from receiver
unsigned long successCount = 0;
//////////////////////////////////////////////////////////////////////////////
char radiopacket[100];


void setup() {
  pinMode(pinBuiltInLED, OUTPUT);

  // Make sure the Xenon is not connected to the Mesh network
  Mesh.disconnect();
  Mesh.off(); 
  Particle.disconnect(); 
  BLE.off();

  pinMode(RFM95_RST, OUTPUT);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);
  // manual reset  (pull low for 100 us)
  digitalWrite(RFM95_RST, LOW);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);

  while (!manager.init()) {
    while (1) blinkERR(pinBuiltInLED);
  }
  // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM
  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
  if (!rf95.setFrequency(RF95_FREQ)) {
    while (1) blinkERR(pinBuiltInLED);
  }
  // The default transmitter power is 13dBm, using PA_BOOST.
  // If you are using RFM95/96/97/98 modules which uses the 
  // PA_BOOST transmitter pin, then you can set transmitter 
  // powers from 5 to 23 dBm:
  rf95.setTxPower(13, false);
  //////////////////////////////////////////////////////////////////////////////

} // setup()


void loop() {
  // Send a message at a random interval between 1000 and 5000 ms
  if (timerLast > millis())  timerLast = millis();
  if ((millis() - timerLast) > timerInterval) {
    blinkLED(pinBuiltInLED);
 
    snprintf(radiopacket, sizeof(radiopacket),"aFloat:,%1.2f",9.9);
    radiopacket[strlen(radiopacket)] = '\0';
    
    digitalWrite(pinBuiltInLED, HIGH);
    if (manager.sendtoWait((uint8_t*)radiopacket, sizeof(radiopacket), RECEIVER_ADDRESS)) {
      digitalWrite(pinBuiltInLED, LOW); 
      // The code never returns from manager.sendtoWait()
    } else {
      // error
      while (1) blinkERR(pinBuiltInLED);
    }
    timerInterval = random(1000,5000);  
    timerLast = millis();
  } // timer

} // loop()

void blinkLED(byte ledPIN){
  //  consumes 300 ms.
  for(int i = 5; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(30);
    digitalWrite(ledPIN, LOW);
    delay(30);
  }    
} // blinkLED()


void blinkERR(byte ledPIN){
  // S-O-S
  const int S = 150, O = 300;
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(O);
    digitalWrite(ledPIN, LOW);
    delay(O);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
} // blinkERR()


@markwhiehl,
Your code worked fine for me after I actually added the relevant library via the WEB IDE library mechanism-

// This #include statement was automatically added by the Particle IDE.
#include <RF9X-RK.h>

and using Serial.begin(115200); in setup and
a Serial.println statement right after the line

if (manager.sendtoWait(…
verifies repeated transmission (along with receipt by the server).