Riot: Proposal: eSAUL - Extended Sensor Actuator Uber Layer

Created on 23 May 2020  Â·  41Comments  Â·  Source: RIOT-OS/RIOT

Description

SAUL offers getter and setter to control actuators and obtain sensor readings. It is completely up to the application using SAUL when to access the SAUL device. This can be a limitation as the application may wants to react on sensor events.

Examples:

  • A toggle button is pressed
  • A temperature changes significantly
  • A motion sensor detects significant motion

Of course, this may be implemented by polling these sensors. But hardware interrupts may allow more elegant and better solutions with low latency.

This made me thinking of a more sophisticated solution: eSAUL. It extends the existing SAUL registry by a maintenance thread that checks supported eSAUL devices in terms of changes and notifies interested threads.

Has anyone already put some thought in solutions like this? What do you think of such a system? Do you have use-cases which could benefit from eSAUL?

Some implementation ideas
  • The saul_driver_t is extended by an check_change function pointer -> esaul_driver_t. If this function is called, it checks if the sensor reading has changed since the last call and returns the current sensor reading.
  • eSAUL requires one thread for maintaining all eSAUL devices:

    • It listens for MSG_UPDATE_DEVICE messages using msg_receive(). These messages trigger calling the check_change function defined in the respective esaul_driver_t.

    • If check_change returns a non-zero value, a MSG_BUS_CHANGE is posted using msg_bus_post(). Other threads may subscribe to these change events and listen for the new sensor readings

  • During initialization of a driver supporting eSAUL, it:

    • registers the device with the SAUL registry -> eSAUL devices are also SAUL devices.

    • may implement IRQ handlers which sends MSG_UPDATE_DEVICE messages.

    • may start a recurrent timer which sends MSG_UPDATE_DEVICE messages periodically.

If this sounds interesting to someone, please let me know. In this case, I'll open a PR with a PoC demonstrating this proposal.

Useful links

SAUL documentation: https://riot-os.org/api/group__drivers__saul.html

13154

SAUL enhancement

Most helpful comment

This is actually a really interesting use-case. I think it could be useful to introduce an API where you can register pointer functions. These pointer functions get called when a specific event occurs. Similar to the event handling in Javascript.

Hmm, I know and like this concept, as well ;) I'm quite familiar with JavaScript.

Have you already thought about which stack to use to call such pointer functions? The obvious solution would be to run the pointer function on the caller stack. But in this case it becomes unpredictable how large the stack must be.

That is why I would prefer the IPC messages over calling function pointers. So it's up the thread that wants to be notified to reserve enough stack space. And the message bus I am talking about is already present: #13947.

All 41 comments

This is actually a really interesting use-case. I think it could be useful to introduce an API where you can register pointer functions. These pointer functions get called when a specific event occurs. Similar to the event handling in Javascript.

This is actually a really interesting use-case. I think it could be useful to introduce an API where you can register pointer functions. These pointer functions get called when a specific event occurs. Similar to the event handling in Javascript.

Hmm, I know and like this concept, as well ;) I'm quite familiar with JavaScript.

Have you already thought about which stack to use to call such pointer functions? The obvious solution would be to run the pointer function on the caller stack. But in this case it becomes unpredictable how large the stack must be.

That is why I would prefer the IPC messages over calling function pointers. So it's up the thread that wants to be notified to reserve enough stack space. And the message bus I am talking about is already present: #13947.

@jue89 That's a good point. It introduces a bit overhead, but has some other benefits as well. Especially when I think about separation of concerns.

Let's start with the bikeshedding :-D Why not just add a submodule to SAUL? The name "Sensor and Actuator Uber Layer" remains still accurate. A distinct submodule however makes sense, as this is a distinct feature with additional resource requirements. Maybe something like saul_observe, saul_notify, saul_subscribe, ...?

The choice of the message bus seems a perfect fit for this. IMO, a global msg_bus_t saul_bus that is initialized from auto_init() fits the bill. The notifications for actuators can easily be done from within SAUL (this would ignore low level accesses from the driver specific API, but would free actuators from even caring about notifications). For sensors, I would just leave it up to the sensor drivers to implement the notification.

The interesting point is what should the message contain? My ideas are the following:

  1. Just a pointer to the saul_reg_t entry of the sensor/actuator that has a change in value

    1. Pro: Trivial to implement, no additional memory required, as the saul_reg_t is allocated anyway

    2. Con: Subscribers would than have to explicitly call saul_reg_read() to get the new value, if they are interested.

  2. A new type that already contains the value

    1. Pro: Multiple calls to saul_reg_read() are avoided

    2. Con: Where would that be allocated? One static allocated value per driver? What happens if the subscriber accesses that value after the driver has posted the next event (and, thus, overwritten the old value)?

Also, the filtering feature of the message bus would be nice to exploit. However, there are way more sensor and actuator types than 32. Maybe just filter by class (so subscription needs to be done separately for sensors and actuators)? Some convenience function could be provided that filter the received messages by saul_driver_t::type on the calling side.

I agree that this should be IPC-based to better manage stack sizes.

In the light of the gcoap wrapping, the IPC messages could be level-based. (And without any processing in the interrupt handler or an overflowable queue, I think it always is). In that case, it might make more sense to not use messages, but to register a (PID, flag) combination to a SAUL sensor, and define the receiver's reaction as "clear the flag, and read the value".

Would it make sense to have a saul_notify_poll function that processes all SAUL registrations and sends the messages / sets the flags? Having them in a task may then just be a convenient default.

Would such a call be necessary in all cases? I figure that most drivers (all as currently implemented) would just store their last values and set the flags, but some drivers might expose additional members on their driver structs for registration added and registration removed and just set up interrupt handlers to set the flags in those cases, to the point where they don't need a thread / periodic calling any more. (As I understand the current proposal, this would be an afterthought and out of scope, but I'd like to have that confirmed.)

In that case, it might make more sense to not use messages, but to register a (PID, flag) combination to a SAUL sensor, and define the receiver's reaction as "clear the flag, and read the value".

I don't get this. If there are no messages, what is the receiver receiving?

The receiver would receive just an indication that the sensor it ordered notifications from has changed, and that it should read it for further processing. A receiver that always reads several sensors in one go (say, a dew point calculation that reads temperature and humidity) might configure multiple sensors to hit the same flag, but most applications would put each sensor they subscribe to into a dedicated flag bit.

(Granted, this would fly best with an additional "read latest updated value" that just hits the comparison value rather than triggering a read for costly sensors.)

The receiver would receive just an indication

That indication needs to be implemented somehow. core_msg is widely used, very stable and quite efficient, so that IMO would be a suitable way to implement that indication. Still, the message bus on top is quite new and not yet widely used and as thouroghly tested, but it is a rather slim layer on top. And it is not too difficult to image use cases for multiple subscribers for a single sensor. (E.g. a smart watch with the UI code being one subscriber and a CoAP interface to it being a second subscriber. Or a smart media station with the UI again one subscriber and CoAP interface the second.)

I like the idea to subscribe for individual sensors instead of having one global bus (or other IPC mechanism), as no filtering of messages (or indications) is needed. However, most hardware has few sensors and actuators, so filtering shouldn't be too expensive CPU wise. And only having to allocate the state one subscription per subscribers safes some RAM. So this is a trade-off.

Ad individual sensors, handling will need to be per sensor in some way anyway -- otherwise a single observation on, say. the cheap-to-observe push button will make the eSAUL thread poll expensive bus-wired sensors with no need.

My concern about messages is what's happening when queues overflow. A level-based IPC like process flags will ensure the behavior which saul_gcoap will expect -- intermediate values might be lost, but the last is the latest. In a message approach (as I understand, no matter whether regular msg or msg bus), you might lose the last messages. In the smartwatch example, if its knob sits on a rotary encoder, the moment you start turning it may spit out 4 messages, fill the queue, and not forward the following 5-6 events that indicate its eventual resting position.

In xtimer, we're offering several ways for an event receiver to set itself up -- as a message, as a flag, at a mutex or through a signal. Do we want to pick one of those, or have the same generality? (Neat as it'd be to have abstraction over those, that may not be immediately possible, for the xtimer only fires one event, whereas subscription to an eSAUL change would be in an (intrusively?) linked list of listeners, unless we go with the message bus.)

In a message approach (as I understand, no matter whether regular msg or msg bus), you might lose the last messages.

This might not be an issue. If the message contains a pointer to the saul_reg_t and not the actual value, the subscriber will still need to manually read the actual value. In that case the subscriber will always have the lasted value and will potentially loose intermediate ones.

In xtimer, we're offering several ways for an event receiver to set itself up -- as a message, as a flag, at a mutex or through a signal.

From the high level perspective this is true. But from the low level perspective xtimer will always call a callback function in interrupt context. There are utility functions that implement well-known callback functions that will in a second stage wake up a thread, unlock a mutex, send message, ...

I think we should do the same here: Implement only one IPC mechanism that fits the standard use case best. If more are indeed needed, those mechanisms can provided as utility functions on top.

My concern about messages is what's happening when queues overflow.

this.

May I suggest an extension to sys/event, where an event source (eg, sensor) has "event hooks", a list of events (event_t) that it just executes in sequence?

A list of events is just one pointer, as for msg_queues. But with an event, the user would have full control on what happens if the event gets triggered. Even sending a message is possible, or forwarding the event to another event_queue to process it in a different context.

I think we should do the same here: Implement only one IPC mechanism that fits the standard use case best. If more are indeed needed, those mechanisms can provided as utility functions on top.

yup.

May I suggest an extension to sys/event

I played with this idea a while ago and got some code lying around. I'll try to PR it by tomorrow, so we can discuss it.

Thank you very much for your valuable feedback!

First thoughts:

Let's start with the bikeshedding :-D Why not just add a submodule to SAUL? The name "Sensor and Actuator Uber Layer" remains still accurate. A distinct submodule however makes sense, as this is a distinct feature with additional resource requirements. Maybe something like saul_observe, saul_notify, saul_subscribe, ...?

Good idea! But for now I'd like to stick with eSAUL as a working title. I don't want to create confusion at the current stage of this proposal.

The interesting point is what should the message contain? My ideas are the following:

  1. Just a pointer to the saul_reg_t entry of the sensor/actuator that has a change in value

    1. Pro: Trivial to implement, no additional memory required, as the saul_reg_t is allocated anyway
    2. Con: Subscribers would than have to explicitly call saul_reg_read() to get the new value, if they are interested.
  2. A new type that already contains the value

    1. Pro: Multiple calls to saul_reg_read() are avoided
    2. Con: Where would that be allocated? One static allocated value per driver? What happens if the subscriber accesses that value _after_ the driver has posted the next event (and, thus, overwritten the old value)?

Good question! My first thought: Pick the easy solution and make the notification receiver calling saul_reg_read(). Drivers may implement caching here if this read call is too expensive to be called very often.

This way threads using eSAUL may implement periodic read out of sensors by setting up a timer that sends the same event type to the message queue.

Also, the filtering feature of the message bus would be nice to exploit. However, there are way more sensor and actuator types than 32. Maybe just filter by class (so subscription needs to be done separately for sensors and actuators)? Some convenience function could be provided that filter the received messages by saul_driver_t::type on the calling side.

Or map the saul_reg_t pointer address to a number in the interval 0..31? Collisions may occur, but this would lead to a pre-selection und reduces noise. (Some kind of bloom filter?!)

My concern about messages is what's happening when queues overflow.

this.

May I suggest an extension to sys/event, where an event source (eg, sensor) has "event hooks", a list of events (event_t) that it just executes in sequence?

A list of events is just one pointer, as for msg_queues. But with an event, the user would have full control on what happens if the event gets triggered. Even sending a message is possible, or forwarding the event to another event_queue to process it in a different context.

Who is owning the event queue? The thread interested in eSAUL change notifications?
Whose stack holds the event? The driver sending the event?
Is one event posted on several event queues?

Or map the saul_reg_t pointer address to a number in the interval 0..31? Collisions may occur, but this would lead to a pre-selection und reduces noise. (Some kind of bloom filter?!)

Good idea. How about instead the number of the element in the list module 32? (Might need caching to avoid O(n) look ups for every event post.)

Btw: If we would get https://github.com/RIOT-OS/RIOT/pull/9105 in, the SAUL registry could be set up at compile time. And getting the element number for a given address within an array with a known start address is pretty cheap :-)

Good idea. How about instead the number of the element in the list module 32? (Might need caching to avoid O(n) look ups for every event post.)

Before relying on the element's index, we need to get rid of saul_reg_rm() first ;)

Before relying on the element's index, we need to get rid of saul_reg_rm() first ;)

Or just add a comment that usage of saul_reg_rm() is also not compatible with this. (We could also replace the warning with the more boiled down statement: "You can either use saul_reg_rm(), or the rest of the SAUL API, but not both. The only exception is saul_reg_add(), which is indeed compatible with saul_reg_rm().")

Also, the filtering feature of the message bus would be nice to exploit. However, there are way more sensor and actuator types than 32. Maybe just filter by class (so subscription needs to be done separately for sensors and actuators)? Some convenience function could be provided that filter the received messages by saul_driver_t::type on the calling side.

Or map the saul_reg_t pointer address to a number in the interval 0..31? Collisions may occur, but this would lead to a pre-selection und reduces noise. (Some kind of bloom filter?!)

We can know at compile time which sensor types are available (sensor drivers would have to select a pseudomodule then for which types they enable).
Then it would be no problem to have a separate bus for each sensor type.

Also, the filtering feature of the message bus would be nice to exploit. However, there are way more sensor and actuator types than 32. Maybe just filter by class (so subscription needs to be done separately for sensors and actuators)? Some convenience function could be provided that filter the received messages by saul_driver_t::type on the calling side.

Or map the saul_reg_t pointer address to a number in the interval 0..31? Collisions may occur, but this would lead to a pre-selection und reduces noise. (Some kind of bloom filter?!)

We can know at compile time which sensor types are available (sensor drivers would have to select a pseudomodule then for which types they enable).
Then it would be no problem to have a separate bus for each sensor type.

Hmmm, the separation per sensor type assumes that in most use cases only one sensor type is present. I think this assumption falls apart if GPIOs, ADCs, ... are modeled as sensors. I still prefer the bloom filter idea. We have to deal with collisions anyway.

Another problem appears if a thread wants to unsubscribe from update events. It must keep track of, whether the event type is used by another sensor.

Hmmm, the separation per sensor type assumes that in most use cases only one sensor type is present.

No, you'd just have a separate bus per sensor type.

So saul_attach(SAUL_SENSE_TEMP, …) would attach you to the temperature bus where you then could subscribe to different temperature related events.

I'm still not convinced the message bus is the right way to distribute those events -- with SAUL the application won't know the board's sensors' update rates and can't guarantee it will process all events without overflow.

In the concrete case of a saul_gcoap server, when there are two observations (say, on a fast temperature scanner and a slow humidity scanner), the thread that sends the notification will spend lots of time waiting for permission to send data. If it's messages that are coming in, it will need to be built in such a way that it can always process the messages inbetween and convert them to its own "this observation needs notification" flags.

The event approach sounds more practical to me -- the subscriber would allocate an event, pass that to the eSAUL thread, and that thread can fire the event (which would not block, and repeated calls before fetched don't do any harm). For eSAUL to support multiple listeners on a sensor, the allocated event would need to be prefixed by an eSAUL header that allows LL'ing the listeners together.

Why not do it like we did with sock_async and make it so that the event front-end (event, msg, msgbus, ...) can be picked by the user?

Why not do it like we did with sock_async and make it so that the
event front-end (event, msg, msgbus, ...) can be picked by the
user?

That's similar to how xtimer works, right? In xtimer there's a lot of
convenience functions (_set_flag, _set_wakeup, _set_msg) whereas in
sock_async there's only _set_cb -- probably just an "API surface vs.
low-threshold usage" thing.

Unlike in those cases, I think we want to allow multiple listeners here.
By the sock_async pattern, that would give an API like

typedef struct saul_subsriber {
    saul_cb_t cb;
    void *data;
    struct saul_subscriber *next;
} saul_subscriber_t;
void saul_subscribe(
    saul_reg_t *sensor,
    saul_cb_t cb,
    void *data,
    saul_subscriber_t *subscriber
);

where cb and data would be stored right into subscriber, and that gets chained into the sensor's subscriber pointer. It'd be up to the caller to keep the subscriber_t around until deregistration.

Why not do it like we did with sock_async and make it so that the event front-end (event, msg, msgbus, ...) can be picked by the user?

So it boils down to a linked-list of callback functions that is attached to a SAUL sensor/actuator, right? These callbacks are called in the context of the eSAUL thread and may send a message, wake-up a thread, ...

Sry, I haven't had a dive into sock_async, yet. This is just an educated guess ;)

The event approach sounds more practical to me -- the subscriber would allocate an event, pass that to the eSAUL thread, and that thread can fire the event (which would not block, and repeated calls before fetched don't do any harm).

I don't understand the semantic difference you see. A message is only delivered to a subscriber if:

a) The subscriber is currently blocked with receiving a message. (Thus, ready for the event)
b) The subscriber set up an message queue and there is still free space for at least one message.

So a subscriber not ready to process the next event would just not miss the message. This is semantically completely equivalent to what @chrysn suggested, isn't it?

Out of those two options, I see no reason to reinvent the wheel rather than just using the message bus.

These callbacks are called in the context of the eSAUL thread and may send a message, wake-up a thread, ...

Yes. (Although that's not what sock_async does -- that only ever has one callback. The linked list would be an extension of that theme to also carry a next pointer to make the LL)

I don't understand the semantic difference you see.

The semantic difference is that a message can get lost (of two distinct messages, only the first is processed if there is overflow), whereas an event always gets set but it's just indistinguishable whether it was set once or multiple times. That makes a difference when a process waits for multiple notifications (say, three temperature sensor readings fill up the queue and the humidity reading gets lost) or when there is data associated with the messages (which can't be in the event case).

I now begin to wonder if the Message Bus was actually a reinvention of the Event Queue wheel

Yes. (Although that's not what sock_async does -- that only ever has one callback. The linked list would be an extension of that theme to also carry a next pointer to make the LL)

Yes, sock_async uses just a single callback, that is supposed to be set by the front-end application (currently only event as a front-end is available). Nothing is speaking however against, that this callback does some multiplexing in the front-end, if need be.

(if, in the case of sock_async, this is sensible, as sockets are typically only read-once, is another question ;-))

I now begin to wonder if the Message Bus was actually a reinvention of the Event Queue wheel

Well, the event queue has 1..n publishers and one subscriber, the message bus has 1..n publishers and 1..n subscribers

whereas an event always gets set but it's just indistinguishable whether it was set once or multiple times.

That would be the exact same behavior when doing:

while (1) {
    msg_t msg;
    msg_receive(&msg);
    phydat_t data;
    saul_reg_read(msg.content.ptr, &data);
    work_on_data(&data);
}

The content of the message does not change, it is just a pointer to the saul_reg_t SAUL registry entry. So there is no difference if I only receive the first, the last, or a message in the middle. And from the usability perspective, it is nice to receive a pointer that can be fed directly into saul_reg_read().

With the approach of setting bits in a bitmask on events, the subscriber would have to translate that bit into the sensor it is interested in. This is both less convenient and less efficient.

For a thread that only does that, it makes no difference. But already if work_on_data forwards the data so some asynchronous receiver in a way that involves message exchange, the message approach may jam the system.

Messages or message queues work well when the receiver knows at all points in time which and how many messages it will receive -- then it can set its own message queue accordingly. If it willingly accepts overflows (say in a network buffer situation), that's also OK but then no other important messages must arrive during that time.

SAUL is designed for portability between boards, and a consumer can't generally know the number of updates to expect if it subscribes to a given sensor -- and thus can not allocate a suitable message buffer.

For a thread that only does that, it makes no difference. But already if work_on_data forwards the data so some asynchronous receiver in a way that involves message exchange, the message approach may jam the system.

So why not have a thread dedicated to handle the messages then and have a separate networking thread.

As I understand, we already plan on having a thread that polls any sensors for which there are subscribers. There'll be application threads that want notifications.

Another thread sitting inbetween would be rather pointless, as it'd need to solve the issue of how to get the events out just all over again -- and what for? Whichever flexibility that has to notify the application threads can just as well be exerted from the original polling thread. (And ideally should be usable from interrupts as well, in case the polling thread is later abandoned in favor of interrupt-driven methods).

The xtimer / sock_async approach already does that, and it can also be used to drive the events onto a message bus if the application author so desires (though I'd recommend against it for the abovementioned reasons).

Or, taking a step back and looking over the whole conversation: Why is msg_bus a candidate for this in the first place? Granted, it works well if large messages are to be sent to multiple receivers, but messages are small here and the receivers are mostly few.

Or, taking a step back and looking over the whole conversation: Why is msg_bus a candidate for this in the first place? Granted, it works well if large messages are to be sent to multiple receivers, but messages are small here and the receivers are mostly few.

I saw a "1 sender to n receivers" pattern. The sender doesn't care who's listening. And I didn't want to implement yet another IPC to solve this.

As I understand, we already plan on having a thread that polls any sensors for which there are subscribers.

Is there really a use case for this? If I want periodic sensor readings, wouldn't the exiting timer API already fit the bill quite nicely? Every subscriber in this scheme likely has different requirements for the reading frequency anyway.

To me, this notification makes only sense for sensors like a motion sensor, a button, etc. which does create IRQs that are translated into higher level event notifications.

it works well if large messages are to be sent to multiple receivers

Sending a message involves copying the pointer/value union (4 bytes), the type of the message (2 bytes), and the sender's pid (2 bytes). The actual content the pointer in the message is pointing to is not transferred. The context switch between threads is also quite light weight. And as I expect events being posted from IRQ context anyway, the context switch is hard to avoid any. (Or do we want the subscribers to be potentially run in IRQ context?)

Being honest: All the IPC mechanisms available should be more than fast enough for whatever events the average sensor can generate. The only case I can imagine a sensor is indeed causing many events at a high frequency is an button without proper de-bouncing. And if we would ship an eSAUL button implementation without de-bouncing, the first thing any user would do is opening an issue complaining that the de-bouncing of the button is broken.

"1 sender to n receivers" pattern

That is a good point, but as I've outlined msg is probably unsuitable. That may illustrate that there is some need for a more generic "dispatch to multiple handlers" tool that's not limited to using the message bus -- or we're just seeing the single instance where that's needed, hard to tell.

As I understand, we already plan on having a thread that polls any sensors for which there are subscribers.
To me, this notification makes only sense for sensors like a motion sensor, a button, etc. which does create IRQs that are translated into higher level event notifications.

Ok, I thought that this interrupt-based thing would be an afterthought only -- fine if it's the main mode of operation. That definitely makes things easier on the side of later obtaining the most recent value from the sensor -- as that's then just a read from the memory location that gets updated in the interrupt.

So a sensor that doesn't push values in interrupts would just not accept subscriptions? That would be the most lightweight thing -- provided it fits the use case for eSAUL.

Or do we want the subscribers to be potentially run in IRQ context?

The xtimer / sock_async approach would in allow the subscriber to run something in the IRQ context, but it would very much be recommended that it'll be only "Release the given mutex", "Set the given thread flag", "Wake the given thread", "Set the given event" or "Send the given message" or a similarly small one. (In xtimer, the arbitrary-callback has big red warnigns; in sock_async, the async API has no large warnings but the expectation seems to be that you use the event-based one which executes the "set the given event" callback).

All the IPC mechanisms available should be more than fast enough for whatever events

The speed of the IPC mechanisms is not what's worrying me. The application's ability to use them reliably in light of fast notification streams compared to the application's update rate is what concerns me, and where approaches that have the message bus as the only way out of an event fall short.

As I understand, we already plan on having a thread that polls any sensors for which there are subscribers.
To me, this notification makes only sense for sensors like a motion sensor, a button, etc. which does create IRQs that are translated into higher level event notifications.

Ok, I thought that this interrupt-based thing would be an afterthought only -- fine if it's the main mode of operation. That definitely makes things easier on the side of later obtaining the most recent value from the sensor -- as that's then just a read from the memory location that gets updated in the interrupt.

So a sensor that doesn't push values in interrupts would just not accept subscriptions? That would be the most lightweight thing -- provided it fits the use case for eSAUL.

I would allow subscriptions for every sensor. But only those implementing eSAUL are going to notify at some time.

The question what triggers a notifications is TBD. I would let the driver's programmer decide when to trigger a notification but give some advise in terms of best practices.


I'll come up with a PoC and create some facts we can work with.
Because the decision which kind of IPC to use for notifications seems to be very controversial, I'd suggest to implement the linked-list of callbacks, first. This way, we can try this driver with messages, events, flags, ... easily.

The application's ability to use them reliably in light of fast notification streams compared to the application's update rate is what concerns me, and where approaches that have the message bus as the only way out of an event fall short.

The behavior of the message bus is well defined in a setting in which the receiver is not ready: The message is dropped. In the context that the message contains only a pointer to the saul_reg_t of the sender, what is the problem with the lost message? (I mean apart one value in the value stream being lost; but that is a problem independent of the used IPC mechanism.)

Also: Is there any indication that a fast notification stream is even remotely plausible for users of a sensor abstraction API? My point is: The SAUL API is IMO not suitable for scenarios where sensors are reading data at a high sampling rate. E.g. let's say I want to build a digital scope using a RIOT running board, an fast ADC, and e.g. an SPI display. Would it be plausible to use SAUL for the ADC, rather than using an low level ADC interface? The most obvious choice would be to set up the ADC to use DMA to transfer the samples to keep the CPU mostly unoccupied from handling the ADC. That way the CPU can draw nice plots that, again using DMA, would be streamed over SPI to the display.

Am I missing any use case where the combination of SAUL and fast sensor sampling rate make any sense?

I'm not saying that I'm 100% convinced the message bus is the final, superior, and ultimate solution to this problem. But I think the discussion is mostly focusing on stuff that I consider irrelevant in the context of the use case.

To me the following aspects are of most concern (in this order):

  1. Suitable for the n publishers to the m subscribers setting; preferrably without any manual looping over a list (but leaving this to the IPC API)
  2. Easy to use (SAUL is a convenient sensor abstraction API, not an API to provide high performance and expose all the features)
  3. Resource requirements

Out of the currently upstream IPC mechanisms, the message bus is a perfect fit in regard to 1) and 2), but might cause in an additional thread being created. I expect that @kaspar030 PR will be a perfect fit for 1. and 3., but async APIs are bit leas convenient to use. I expect @chrysn proposal to be similar to @kaspar030, but that IPC mechanism would be hand-crafted for this specific use case. The event-broadcast system @kaspar030 proposed would be an utility available for other code as well - which IMO is preferable over an special purpose implementation.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions.

What is missing here is kind of an agreement on what is the best, right?

I think the discussion above ended up in all arguments being said. Maybe we just vote and whatever gets the most votes get's in? As always, any of the proposed option is better than not getting this feature in due to not reaching an agreement.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

silkeh picture silkeh  Â·  5Comments

miri64 picture miri64  Â·  3Comments

l3nko picture l3nko  Â·  7Comments

pietrotedeschi picture pietrotedeschi  Â·  4Comments

jcarrano picture jcarrano  Â·  7Comments