Softata: InterCore Communication
The RPi Pico W has 2 cores. The Arduino Pico W BSP supports the intercore synchronization and messaging using a Push-Pop mechanism. Softata runs all IoT Hub communications in a separate thread in the second core but is orchestrated by the main core using commands pushed as a number to the second core.
Links
- Code Repository djaus2/Soft-ata
- More posts on Softata
About
Softata uses the Earle Philhower RPi Pico W BSP. The repository for that BSP is located at earlephilhower-arduino-pico on Github with documentation for its API at API. RPi Pico has two processing cores which can seamlessly run at the same time. The relevant MultiCore Processing API topic is located at here.
One option with sensors in Softata is to continuously read a sensor and display the results literally or as a json string. For these, each read is initiated by one call from the host app. These sensor reads are done in the main thread in the first core. Alternatively, Softata uses the second core to independently run the sensor reads and its telemetry transmission over Bluetooth and to Azure IoT hub. With these, once started by one Softata API call from the host app, these transmissions continue in a timely manner until a pause or stop message is sent from the host.
A classic Arduino app such as that which will run on an Arduino Uno consists of two main functions; the setup() and loop(). These can invoke other functions but the overall flow of control runs once through the setup then continuously re-calls loop each time it completes. The Earle Philhower BSP makes use of the second core in a second independent thread that runs through setup1() once then continuously loops through loop1() and so the program structure for the second core is similar to that for a classic Arduino sketch.
The Arduino Softata sensor IoT Hub and Bluetooth second core code was previously located at the bottom of the main file softata.ino
but has now been moved to a separate file softataCore2.h
a reference to which is included at the bottom of the main file in place of the code that has been moved. The setup1() code sets up relevant data structures and initiates MQTT for IoT Hub telemetry. This latter task is synchronized with the main thread so that it does not start until WiFi is established. loop1() check to see if there are any commands being pushed from the main thread and are actioned if pulled from the intercore stack. Other than these commands, loop1() performs actions such as telemetry sensor reads and transmission the information for which have been placed in linked list.
Intercore Commands
The intercore commands are one direction from the main thread to the second core thread. They are sent by pushing a number representing the command as an unsigned int32 which is then popped from the stack in the second core and interpreted in loop1(), when available. When a command is sent, there are two parts to it: The command and the index of the item in the linked list to which it applies. The commands are:
enum SyncedCommands : byte {pauseTelemetryorBT=0,continueTelemetryorBT=1,stopTelemetryorBT=2,svrConnected=10, initialSynch=137};
Hint: If you don’t put the semicolon at the end of an enum in C/C++ you get a wired error message:
error: multiple types in one declaration
.
#define SynchMultiplier 1000
Data that is pushed from Core1 actually contains two datum: the command and an index. (More about the index later). These two are used to generate one number for transmission according to:
num = index*SynchMultiplier + cmd
. eg:
int num = node->TelemetryStreamNo*SynchMultiplier + pauseTelemetryorBT;
rp2040.fifo.push(num);
These are then separated in the second core using div and mod actions with SynchMultiplier:
if (rp2040.fifo.available())
{
uint32_t val = rp2040.fifo.pop();
int index = val / SynchMultiplier;
SyncedCommands cmd = (SyncedCommands)(val % SynchMultiplier);
// Do commands
}
Startup
As mentioned, Core2 thread waits for a synchronization from Core1 before establishing MQTT after the main thread has started WiFi. It does not need connecting to the service for this though. The steps are:
In Core1 setup():
uint32_t val = initialSynch;
rp2040.fifo.push(val);
uint32_t sync = rp2040.fifo.pop(); // This is a wait
if(val==sync)
Serial.println("Core1-Core2 Setup Sync OK");
else
Serial.println("Core1-Core2 Setup Sync Fail");
In Core2 setup1():
uint32_t sync = rp2040.fifo.pop(); // This is a wait
if(doingIoTHub)
{
establishConnection(); //MQTT
}
Serial.println("==== 2nd Core Ready ====");
rp2040.fifo.push(sync);
Starting an IoT Hub Telemetry Transmission
Once enabled a transmission to Azure IoT Hub or Bluetooth occurs as a call from loop1() if the required time has passed since the previous transmission.
To start a transmission as call is mad from the host (C# app:
deviceLinkedListIndex = (byte)SoftataLib.Sensor.SetupDefault(AppState.ISensor);
AppState.ISensor
specifies the sensor type (DHT11, BME280 etc). The deviceLinkedListIndex
is the index referred to in the commands above. With this call an entry is made in a linked list returning the index to it in the list. Commands then use this so the correct device is impacted by the command (pause,continue,stop).
When the call is received by the service the main thread creates an instance of the particular sensor class with default connectivity (custom connectivity could be used). This is then added to list:
int index = AddSensorToList(grove_Sensor);
This is the index that is passed back the the host as deviceLinkedListIndex and is a parameter to all subsequent commands to the sensor in loop1() from the host app/SoftaLib.
The SoftataLib Sensor API
public static int SetupDefault(byte sensorType);
public static double[]? ReadAll(byte linkedListNo);
public static double? Read(byte linkedListNo, byte property);
public static string GetTelemetry(byte linkedListNo); //Nb json string
public static string StartSendingTelemetryBT(byte linkedListNo, byte period = 5);
public static string StartSendingTelemetryToIoTHub(byte linkedListNo);
public static string PauseSendTelemetry(byte linkedListNo);
public static string ContinueSendTelemetry(byte linkedListNo);
public static string StopSendingTelemetry(byte linkedListNo);
Conclusion
Softata provides for a high level C# API for remotely actioning sensor reads on a RPi Pico W. This includes starting, pausing and stopping periodic telemetry transmissions to an Azure IoT Hub or over Bluetooth. The telemetry runs on a second thread in the second core on the Pico W with synchronization when required with the main thread on the first core.
Topic | Subtopic | |
This Category Links | ||
Category: | Softata Index: | Softata |
Next: > | Softata | About (Latest) |
< Prev: | Softata | Adding a new device |