Thank you very much for the guidance @rickkas7! If I’m understanding you right, as long as I only call RF95 and other RadioHead methods from the dedicated LoRa thread then it should be fine. To accommodate that, I changed up the FSM slightly. I now have this running in the LoRa thread:
This ensured all calls into the Radio Head library are all initiated from the dedicate LoRa Thread. From the main thread I just set a slpRqst variable = true. This transitions the LoRa FSM running in the separate thread to the sleep prep state where it can call the rf95.sleep().
I.e. from the main thread, I’ll set the variable to true or false if I want to sleep or not:
lora::instance().slpRqst = true;
and then from the LoRa threadFunction() I’ll evaluate the same Boolean variable to advance the FSM.
case st_LoraMsgRx:
{
// If the main thread is telling us to sleep, then enter the sleep prep state where we turn the radio off using RF95.sleep()
if (slpRqst){
st_newLoRaState = st_LoRaSlpPrep;
}
Is this an acceptable way to handshake between the main thread and a separate worker thread?
What about adding the data that came from a LoRa message to a JSON Array? Is there any concern in writing to and reading from a public JsonWriterStatic object concurrently from two threads? Does that happen so fast that threads won’t collide? Do I need to protect that using lock() and unlock() anytime I use the JsonWriterStatic object from either thread?
For example: When a LoRa message is received, I first construct a JSON object of that LoRa message payload. I then call a method from a different singleton class to add it to an aggregated JSON Array.
This is located in the LoRa thread to first construct the JSON object:
JsonWriterStatic<256> jw;
{
JsonWriterAutoObject obj(&jw);
jw.insertKeyValue(String(cfg.dataPtID.DevReadUTC), Time.now());
jw.insertKeyValue(String(cfg.dataPtID.LoraBase64), encoded);
jw.insertKeyValue(String(cfg.dataPtID.rssiRx), rf95.lastRssi());
jw.insertKeyValue(String(cfg.dataPtID.snrRx), rf95.lastSNR());
}
particle_fn::instance().addJsonMsg(jw.getBuffer(), jw.getOffset());
Which then calls this method from a singleton class that has the JSONArray declared as public member of the class:
//Define Buffer for JSON Array and Initialize it
JsonWriterStatic<1024> JSONArray;
And this is my addJsonMsg() method within that class:
/*******************************************************************************
* Function Name : addJsonMsg()
*******************************************************************************/
void particle_fn::addJsonMsg(const char *json, uint16_t json_len){
//If adding the new JSON object extends the array beyond the max bytes of a Publish event, close the array, publish it, clear it and start another array
if ((cfg.maxEventDataSize - int(JSONArray.getOffset()) - int(json_len)) < 8 ){
JSONArray.finishObjectOrArray();
PublishQueuePosix::instance().publish("DataArray", JSONArray.getBuffer(), PRIVATE | WITH_ACK);
JSONArray.init();
JSONArray.startArray();
}
//Add new data to the JSON Array
JSONArray.insertCheckSeparator();
JSONArray.insertJson(json);
}
Do I need to add lock() and unlock() around this somewhere or any thread protection needed for this example? The main thread can also call addJsonMsg(). Any concerns on the side of publishing data from LoRa messages or is this fairly safe as well?
At the moment I’m waking up, listening to LoRa messages in the separate thread, publishing data and falling back to sleep successfully with no obvious issues. Just trying to avoid any unusual/difficult scenarios by doing the due diligence now on the best way to structure this to be robust.