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).

Did you ever get your code to work? I'm have the same problems you've been having with sendtoWait() hanging up. Right now I'm using device OS 1.5.0. The only thing that seems odd to me is that some of the files in the library ( name=RF9X-RK, version=0.0.2, author=rickkas7@rickkas7.com) are empty. Namely: RF9X-RK.h and RF9X-RK.cpp) I pulled the libraries both from the WebIDE, Workbench and GitHub. All the same.

Thanks.

I never resolved the issue. I am using the latest library and I tried every option that was suggested to me. I’m glad to hear I’m not alone, but sorry you are experiencing the issue as well. LoRa could offer a great post mesh abandonment option for the Xenon if we could figure out the issue.

I completely agree. I’ll dig a bit deeper today. Let you know if I have any luck. Thanks.

@markwkiehl and @TheBigKahuna
Puzzled by your issue with this, I tested @markwkiehl code under OS1.5.0 and found it did indeed hang. I had been using OS1.4.4 where it worked nicely. So I would suggest you downgrade to OS1.4.4 and see if that gets you going.

Thanks. I’ll try OS1.4.4 first thing tomorrow. I spent most of the day on this with no luck. Looks like the interrupt handler works. However when it reads the irq flags (SPIread) to see if the prior transmit completed, the flags come back as 0x0. Doesn’t look quite right.