The responses I've received thus far have helped me formulate this TLDR:
Currently the Observer Pattern is implemented to get information from the sensor-actuator modules to the individual Bluetooth services and LwM2M objects. The actuators should also be controllable from Bluetooth/LwM2M. This can be solved by just having all individual Bluetooth services / LwM2M objects have direct connections to the sensor-actuator modules.
This leads to very many links between modules, whereas many of these services/objects require the same data. This approach seems unscalable and ugly when services/objects require the same data.
So the problem (which perhaps is just a design flaw in the services/objects design):
When trying to reduce the amount of links between the modules and services/objects by moving the data acquisition up a layer, and dispatching that data to the dependent services/objects, the actuator-part becomes harder, because the services/modules lost their direct link to those sensor-actuator modules.
Original (long) problem description:
I have a system with two interfaces to the outside world: LwM2M and Bluetooth.
The system is Zephyr RTOS based, and both Bluetooth and LwM2M should send notifications matching with what they receive from the data-providing threads that obtain data from the (hardware) modules they control. Also, the Bluetooth and LwM2M interfaces can trigger actuators on the system. (All communication is bi-directional)
In particular: Bluetooth has to read a battery level from the battery management thread. The system also has another sensor-actuator service, from which the latest readings should be obtained and the actuator should be controllable from Bluetooth. No problems here, the services could just include the header file containing the API for the required modules. The services do not overlap in which threads they access.
The problem comes with LwM2M. Some of the objects require information and should be able to trigger actions from the same threads. I could let all objects directly interface from/to the required threads, but perhaps it is more elegant to let a single LwM2M module talk with the other threads, and act as a bridge between the objects and the threads. However, this would require much of the internals of the objects to be exposed, and extra communication because of the middle-man module.
I have made a simplified schematic overview of my situation to help illustrate my struggle (Sensor-actuator 2 goes to two (actually more) different LwM2M objects):
What would be the most elegant way to solve this? I would like to have a similar solution for both Bluetooth and LwM2M (perhaps this is my mistake).
I have thought and partially implemented the following ideas, but each time I end up discouraged that it's the best approach:
- The different Bluetooth services and LwM2M objects all have direct access to the required threads. Programming seems relatively easy, but this introduces many direct dependencies / accesses to overlapping data from the target modules/threads.
- Have the main Bluetooth module and the main LwM2M module control all the data flow between the services /objects and the target modules/threads. This would reduce the data-read dependencies between the services/objects and the target modules/threads, but it increases complexity quite severely when data is received from Bluetooth/LwM2M and something has to through the additional main Bluetooth/LwM2M module.
- Have data-reads go through the Bluetooth and main LwM2M modules, such that those two modules listen for important data-updates from the modules/threads, which then update the services/objects. But as opposed to the first option, also add direct access in the services/objects so that they can directly trigger actions from the target modules/threads. This also feels odd. Programming is likely quite easy in this way, but there are more direct dependencies from/to the modules/threads.
I'm sure I'm overlooking a better and more elegant solution, I would very much appreciate your insight!