Enhancing a project: sensor monitoring

I am looking for some suggestions to build on top of the existing application as an enhancement.

The application leverages Observer design pattern and acts more like a temperature monitoring through subscriptions; as long as user is subscribed to temperature readings, every X seconds or so a temperature value will be read off the sensor, and broadcast to it the UART port + the BLE service.

The high level design includes:

  1. TemperatureSensor: contains a Run() task that reads and publishes a sensor value continuously
void TemperatureSensor::Run()
{
    while(true)
    {
        Read(); // send a read I2C byte
      
       // block until signalled from within ISR that RX transmission is complete
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);  
    
        Notify();	    // notify its subscribers that the value is ready to be read
        vTaskDelay(pdMS_TO_TICKS(DELAY_PER_READ));
    }
}
  1. SystemTask: contains a Run task that’s blocked on a queue. Currently, it’s only unblocked by UART RX and BLE RX messages i.e, user inputs
void SystemTask::Run()
{
    InitializeModules();
    while(true)
    {
        SystemTask::Message msg; 

        if (xQueueReceive(taskQueue, &msg, portMAX_DELAY) == pdPASS)      
        {
            /* check whether msg contains anything related to subscribe or unsubscribe;
            if unsubscribe: block the Temperature sensor task indefinitely until a request comes to subscribe again...
            */
    }
}

That’s mostly the gist. The rest is low-level details. I’d like to leverage more of FreeRTOS primitives though 1) for learning 2) for making the application perhaps more complex.

One thing I thought of was to add a functionality to be able to view the sensor data in real-time on a webpage but 1) I may require an internet connection on my MCU which I’m not sure about 2) doesn’t involve much embedded programming

Any ideas would be greatly appreciated.

It’s a little unclear to me wha you are trying to do, and what you mean by “subscribe”. If you mean subscriptions WITHIN your device, so that perhaps the serial port, or the bluetooth device, or something else could “subscribe” to temperature notifications, then I would have the Temperature Sensor task be structured something like:

Have a list/array of subscribers to the service, each entry being a call-back function that the sensor calls with new data, that will somehow queue up that data for its subscriber and return quickly.

The sensor task then at the desired rate read the temperature (and my I2C read calls are blocking and don’t return until the answer is available), and then call each call-back function in that list/array with the new data. There may be an optimization where if there are no subscribers we stop reading.

The API will be a subscribe function that takes the address of the call-back function, and an unsubscribe function who might take the return value of the subscribe function to mark where the subscription was stored, or might just take the same function and search for the entry. This largely isolates the details of how the subscription is done from the subscribers.

For my cases, the TemperatureSensor is a class, and the subscribe/unsubscribe api are member functions of that class.

The Serial Port task then can accept subscription/unsubscription requests and forward as needed to the Temperature Sensor.

The Blue-Tooth class, if it allows multiple connections, would need to keep track of what connected devices have requested temperatures (maybe all connected devices automatically subscribe?) This logic does not belong in the Temperature Sensor task, but the Blue-Tooth task.

I try to avoid a “System” task that is just a mailman for other operations. If its a “UI Processor” that gets messages from equivalent sources, maybe, but I find that UI Processors want there processing to be about the input stream that the command came in on, which is where a base class can be useful.

Expanding your project without internet connectivity will be a bit tricky. A lot of these devices are built for IoT applications and for this reason rely upon the internet for displaying and processing data.

A small update that comes to mind would be to switch to direct to task notifications instead of using a queue for temperature readings. This may not be advisable if your temperature value cannot be represented in 32 bits.

Now, if you’re trying to avoid internet connectivity you could also snag something like this LED display and wire up your board to send a temperature readout over I2C. In this way, you could display the immediate temperature reading. From there it wouldn’t be too terribly complicated to calculate a running average and display that too.

With this said - IoT devices are much more interesting when you add the Internet into the mix. We have libraries which will both help you setup MQTT (or agentless) and TCP. Integration examples may already even exist depending on what board you’re using. When you get connectivity and messaging down, you open up a world of possibilities including over the air updates, data visualization/processing, machine learning and devices communicating with other devices remotely.

Note that I am not using message queues for temperature readings; the readings are published to its subscribers (UART/BLE layers) through an Observer design pattern. There’s a queue used in a SystemTask that gets unblocked from a higher-UART layer once a complete input has been requested…

And yes internet is certainly an important piece of an IoT project. I’ll look into it. Thanks

One way would be something like this:

struct sensor {

char sensor_name[100];
bool subscribe;
list *notifier_callbacks;

};

struct sensor_data[num_sesors];
- This can be present in a shared memeory and hence required critical section locks.

Define a sensor structure which has the following:

subscribe - a bit to check if at least one task has subscibed to the same.
notifier_callbacks - A linked list which holds the notifier callback functions.

Probable design and pseudo code:
2 tasks to be created:
1. one to read the sensor data from hardware and write to sensor_data. This task Triggered by hardware interrupts
2. second one to read from “sensor_data” and check the “subscribe” flag of each sensor
2.1 If the “subscribe” is set, the traverse the “notifier_callbacks” list and call all the callbacks. This can be like a bottom-half triggered by the Task 1 after the data is updated in “sensor_data”.

You can refer to FreeRTOS Features - FreeRTOS for the APIs for task creation, critical section etc.

Much depends on the characteristics of the application. In my case, I get massive amounts of sensor data, so I can’t afford to copy it all over the place. My solution was to implement a ping-pong buffer with one writer/multiple reader locks:

Readers–writer lock implementation based on:
Concurrent control with “readers” and “writers”
October 1971 Communications of the ACM 14(10):667-668
Pierre-Jacques Courtois
F. Heymans
David Parnas

I think I might have seen something recently about an implementation of that being standardized and possibly added to FreeRTOS. Does that ring a bell for anyone?

Anyway, the writer (i.e., the data collector) is real-time and shouldn’t be delayed from its schedule, so it uses Event Groups (or “Flags”) to indicate available new data in one half of the double buffer. Observers can use those flags and the locking mechanism to retrieve data from the buffer. In my case, one of the observers was a Bluetooth interface. Another is a GUI. The observers can come and go without impacting the writer (unless they hold a read lock for too long, which would be bug). Multiple readers can access a half-double-buffer simultaneously.