Embedded C client
It is proposed to create a new C client specifically to work on the smallest embedded platforms. The requirements are:
- very small - able to run in 10k?
- portable to arbitrary system APIs for everything (threading, networking, memory management, persistence)
- any maintenance features would be focussed on the developer rather than the end user
- asynchronous API only (I considered a single-thread only API - would that be useful?)
- pluggable modules for networking, threading, memory management, persistence
- ANSI standard C still, for maximum portability
- simple threading model, say one thread per client object, as simplicity is top priority
Notes from Frank Pagliughi
> I was thinking about having a separate codebase in Paho
Yes, I was thinking a split would be good, because the constraints on a a really small system would be painful for someone with a more capable OS. Regardless of the size/power of the board, the two things I look for are (1) whether the OS has standard API's like Posix and BSD sockets, and (2) whether the chip has an MMU and supports virtual memory.
> some sort of pluggable lower layer so it was easy to port to different networking and other system libraries
Yes. It's pretty easy to write a portable layer around the basic OS support for a small system that has a threaded RTOS. Most have similar capabilities. The one distinction that makes a big difference (though I may be wandering into the weeds here) is whether the OS supports Condition Variables. If so, they are a great building block for everything else. Otherwise the system requires a higher-level work-around.
As for the networking model, many libraries try to mimic the BSD socket interface with varying levels of success. But that should always be the default options. Many libraries also have a lower-level, proprietary API. LwIP is a perfect example. The decision would need to be made whether to target the upper or lower level API's for these libraries.
> a simple threading model, say one thread per client object, as simplicity is top priority
Yes, and also remember that many little RTOS' are strictly prioritized, and some do not even allow two threads to be the same priority. So we would need to make sure that the threads can be provided individual priorities and support cases where they can or can not be assigned the same or priority.
Another decision is whether to keep/provide separate Synchronous and Asynchronous API's. I think that would be wise. On bare-metal systems without an RTOS, it may be sufficient or necessary to provide just a simple, synchronous interface. We should make sure that the Synchronous library could be compiled separately without a lot of dependencies.
> still using standard C, for maximum portability and small size (is that still a benefit?)
Yes. I haven't seen a system in years that doesn't support standard C.
The main thing to keep in mind is that for a small system lacking an MMU, the bane of the programmer is dynamic memory. Using malloc will fragment the heap and eventually fail. So malloc should not be used directly, and any allocation should be done from within a pluggable layer that can be swapped out. The choices might be something like:
- Don't do any allocation in the MQTT lib. The app must supply all buffers. Keep copying to a minimum.
- Provide an allocation layer that uses malloc/free as a default reference implementation and let the user drop-in a required replacement for their system
- Provide a basic fixed-size block allocator which can be swapped out if the system has a better one.