 Yeah, hello everyone and welcome to my talk. My name is Martin Jäger and I'm from LibreSolar which is a project and as well as small company and we develop open source hardware for renewable energy systems, especially tiny solar systems and we also do some contracting work for Zephyr lighted stuff ideally also in the renewable energy sector and Yeah, today I will talk about a project that is not directly related to solar, but It's something we developed During the past years in different projects and it's called things that which is kind of a protocol that is transport agnostic and can be used for many purposes and Yeah, we found it to be very useful in our project and hopefully you can find it useful in your project as well so let's have a look at a Yeah diagram with typical data flows you have in an embedded device. So if we look at the Device in the center you have your MCU with your RAM inside and you often have Some kind of persistent memory like either a flash or an EEPROM or both where you store your settings and Yeah, many devices have some kind of sensor as an input and actuators as an output Where they communicate with which would be simple protocols like SPI or I2C But we also usually have lots of more communication interfaces For our devices for example, it could be a mobile app Communicating via Bluetooth Some kind of cloud connection via MQTT web sockets or co-app for example Yeah, and these are all the communication interface that are kind of relevant to the user of the app But also as a developer we need some other means for communication with the device Usually during development we need some logging That's unidirectional in this picture. So it's just eight output information But often we also need to interact with the device during testing and go into some test settings Yeah, tune some set points. So that would be by directional interface Where you can for example use the Zephyr shell Yeah, so that would be a serial interface as well as the the log data interface and Looking from the manufacturer's perspective also they have to Yeah, during end-of-line testing somehow put data into the device for example for provisioning of keys for calibration of sensors Yeah, end-of-line testing and so on and all these Different interfaces usually use completely different protocols and there are solutions for all of them, but What if we could use exactly the same protocol and the same data for all of these interfaces? and that's exactly what the things that project is about and I'm going to tell you about the yeah, how it works and Yeah, how you can use it for your projects First of all, it's all open source. So you can download all the software and the app that I'm going to show at the end Which is written in Flutter the software is all in C and Zephyr based of course Yeah, so that's the The points I will try to cover in the presentation the first step In order to make the data useful is to have a very defined structure of the data We call it the semantic data model Then we have the protocol that allows to access this data It's kind of a query language. You will see later on but a very simple one Yeah, then we have the different transports because I said it's transport agnostic So there must be at least a few transports it can be used with and some mappings which are Protocols where it can be integrated, but which do not fit for all features of the things that protocol Yeah, at the end I will show how it's all integrated with Zephyr and which features we leveraged and Show it in a demo where we develop a small sensor. That's connected to an app and the app will display useful data Okay, yeah, I wanted to come up with some sort of example application So I thought a smart thermostat could be something useful because we have a simple sensor like a temperature sensor an actuator and Yeah, we need to set some target values I didn't find any picture that was vendor neutral So they all had logos on them. So I asked a stable diffusion for a thermostat in Prague at sunset That's what came out. So that's what we are going to develop now And take as an example because examples are usually better to understand the concepts behind it So, yeah, we have measured temperature as an input a target temperature as an input as well by the user So that should be configurable by an app and the output is whether the heater is on or off Now let's go directly to the data model. So this is Yeah, displayed as a Jason structure and Jason is actually one of the formats we use for the transport But we can also use co-op for more constrained devices and then yeah You send a binary send binary data and you can even use IDs instead of the names here There are a few terms to clarify So one is a data object. That's all of these single lines basically And then we have basically four types of data objects. One is the data item. That's actually useful data so the Data like a measurement value or in this case if you look at the first line It's the node ID. So that's a string and that's a leaf node of the tree like structure you see Then a group is something to gather some useful data into one group as the name suggests Like we can use it for sensor data or For controlled data. So it's just to to have a better structure of the whole thing Then we have something that's called subsets and Subsets are used to reference existing data in the structure so that you can Use only a subset of the whole data to generate reports regular reports that you want to send out to the cloud or via your app And they are basically links to the path inside the Structure so if we look at this subset here the sensor room temperature and degree C is this path so here and then the slash and then this item and An overlay is kind of similar to the Zephyr device tree overlays has a slightly different meaning It's it can hold some meta data. That's not necessary for the Most important understanding of the data But still sometimes you can't code everything into just one single string what you need about the data so that can be put on top of the whole structure of the data and in our case it is used for configuring the reporting of these Subsets of data so you would then create an overlay With the prefix of an underscore and then you have the same name as you have in the original data structure above and then you can Configure how this subset should be reported. We will see that later on and then it will get more clear Now as you have already seen some of those data objects have Prefixes and some do not have prefixes. So the the groups Don't have prefixes and all these naming conventions are really important for the whole model to work and to generate the app out of it at the end and So that's it's just a convention that yeah, if you stick to it You will generate a nice user interface and also the idea is that If you have a device and you don't know anything about it, then you can find out what data it exposes and Most importantly like what unit is the temperature in for example So sometimes you see APIs where there is just temperature and then you think well, is it far and high? Does it degree C? Yeah, and that should all be part of the data I'm yeah, the main objectives of this data model is that it's schema less and self-explanatory So the ideal case would really be that in 20 30 years If that simple protocol that can be used for this for things that is still Useful then you can connect to the device find out the data from the device You don't need any manual or anything like in Modbus for example where you need a huge catalog of register numbers Yeah, that's the probably the most important objective and it should be easy to use in human readable That's why one mode is in Jason and there's a second mode I already briefly touched that that has a more compact footprint and that uses seabor and numeric IDs optionally instead of the String names here, so it can be really used for Laura van for example That's one of our use cases where you have they are like tens of bytes per message And you really don't want to send strings in each message same for can bus That's also one of our main applications You would use the binary ID and then just send the payload value via seabor Quickly touching on these prefixes so our data items the they have For example this P prefix, which means it's a protected item that can usually not be changed by the user And our prefix is a read-only value. So that's suitable for measurement values Because you want don't want to change them And an s is a stored value. That means it's stored in non-volatile memory So our target set point of the thermostat should be stored even if the device runs out of battery It has to be restarted and there's also a W for written values that are stored in RAM only they can be used for control loops where you don't want to write to the eeprom or flash memory every time and Wear it off quite quickly. So here's an overview again about the different prefixes We didn't cover constant So constant are not Changing so if the back end received a constant value it can expect that it stays the same all the time So it only stores it once and not in a time series database for example and read-only values are changing values like measurements Then we have a T prefix for timestamps because they are special and sometimes Yeah sent in every message. So it should be really short and executable items Which call a function internally in the C code for the subsets We also have different prefixes to categorize them That's mainly for back ends so that they understand if this is changing data or is it is it static data That's just sent to the device usually and the groups and the overlays don't have any prefix So you can distinguish them from normal data items now coming to the excess protocol so if we think of this Jason's Structure as something like a database that we have in RAM in our device. We somehow want to access these single items and Yeah, we have two ways to do this one is a request response model and the other one is that you send out regular reports But don't expect a response That's usual useful for the measurement data and also for canvas You would just send it to the bus and don't flood the bus with all the packages Yeah, and the last one is a desire that would be a message that's sent to the device But you don't expect a response either because yeah The device will just try to receive the packet and Pass it and use it and if it can't use it it will be silently ignored or for example if there are data items It doesn't understand it would ignore them Now the protocol is really pretty simple. Here's the basic layout the message layout it Yeah It's just a stream of bytes starting with the first byte which distinguishes What type of message we have so this is just the first byte and it can be either a text byte like the question mark and The question mark would be a get request an equivalent of a get request in HTTP or a co-op in Binary it would be the zero one which is actually the same as the co-op get request Then we have a few other request response functions the Equal sign is to update data Plus is to create new data Data items delete is to delete something from like an array for example And the exclamation mark is an executable eight item responses always use the colon and an additional response code and the response code is mapped to seabor and HTTP response codes so that it can be easily integrated with web environments and so on Okay, let's have a look at some examples of the protocol itself We have the same data structure again at the left and now we want to retrieve all the sensor values So we would send this command to the device. So it's the question mark and then sensor is the path And in this case, it's not nested. It's so it's on the highest level of the hierarchy and Then we will just get back all the JSON that's behind it and that's exactly how the protocol works It takes the values from RAM Puts them serializes them into the JSON structure and sends them out the 85 is the response code for Yeah values with content. It's the 200 in HTTP. I guess Um, yeah, and we have an update request here So we want to update the target temperature and this is a stored value and not a read-only value and So we use the equal sign and the control path and then provide the Yeah, only the data items we want to update in that group and We get 84 as response code which is it has been successfully updated And of course, there are also error responses, which tell you our data object not found and things like that Yeah, and last request response example is to delete the humidity sensor values from the live metric subject subset which would Take so the m life is this live metric subset and it has been this before and This command would delete that sensor out of the list and then it would not be sent out anymore Then we have this other communication pattern the it's more public subscribe like report and desire and That can by the way also be It's more suitable for asynchronous communication. So if you have a server back end and you have some Yeah devices that are not connected regularly You would place the desire in the back end and then the device connects get the desire and applies it to its own data And likewise in the other direction a device can connect to the cloud Then send out the report and then disconnect So here's an example of a report. It also starts with one character in this In case it's the hash sign then it has the sender path again and the JSON structure That's behind this sender path. So that can be used for for a group or you can use it for the Subset in this case the live metric subset So this would be the path and then we don't put this error array anymore, but the actual values that are behind these links and then last but not least the Yeah, a desire to change the control target temperature and Yeah, in this case, we would not get a response as we got previously with the request response message messaging pattern Yeah, the the whole thing can be used over lots of different transport protocols as I mentioned already and This is an example of a possible networking topology. So you could either directly connect to a device For example via Bluetooth or via can so that would be and a desktop application or a mobile app Getting directly onto the device which has Bluetooth and exposed data via things set or you could also have a back end in between and then you could communicate via IP networks for example and Yeah, the back end could forward requests from a web front end or an app as well to the devices that are connected behind the Back ends or you could also have multiple devices or even legacy devices which would for example transfer Modbus Registers into the things that protocol to make the data more useful than just register names But then you have to put the manual information into that gateway Yeah, so Most of these things are already working But I found it would be most interesting to have a small application that Communicates via Bluetooth and that's what I'm going to demonstrate in a few minutes Looking quickly at the time Okay so Yeah, the transport protocols Have only a very basic requirement, which is they must be message oriented bidirectional and Have reliable transport Because we don't do any error checking inside the protocol itself. It relies on error checking in the underlying protocols So of course you can easily use a serial interface, but you could get data corruption So for the serial protocol, we have a CRC. That's part of the serial Implementation but not part of the core things that protocol for web sockets For example, they are really the ideal transport mechanism I would say because they are already have some frames already, so they are message oriented and of course bidirectional HTTP is not very ideal because you don't easily Get the bidirectional communication Yeah for can the message sizes are really small at least for classical can We can only have eight bytes and that's why we use the ISO TP transport protocol Which can hugely increase the the number of Bites you can transfer in one message and then it's also message or message oriented at the same time for Bluetooth Yeah, that's a bit of a tricky case in the Bluetooth classic. There was a Bluetooth serial interface or device Specification, I don't know what the correct term is for Bluetooth Profile, okay for thank you for Bluetooth low energy, there is no such profile anymore and Of course Bluetooth already provides very similar things like we have in our structure You can create different services and characteristics for all your data structure But it's not as flexible as we wanted it to be So our Bluetooth protocol is really simple. We have two services and one service and two characteristics one for sending and one for receiving We have a simple packetization mechanism. So it just Yeah, it tells you where the end of a packet is and then the other end will realize that it's similar to the slip protocol and for serial line communication of IP packets and With that, yeah, we're kind of abusing the the Bluetooth a little bit, but I think that's allowed You can also use it for with SMS We actually did that in an application with 2g modem and you could send basic commands via SMS to the devices with the modem for mappings you can Yeah, use other protocols that are not directly compatible So you can use MQTT for synchronous communication with Request response, but you have to come up with the message ID That's encoded inside the topic and things like that. So that's not the ideal use case So we would say you can map it and for example encode the path We are using in things that into the MQTT topic. That's ideal Yeah, and you can use it for the desires and reports. We just talked about Laura one Of course does not support all the features because it's so low power and low Yeah, data rate But it can be Integrated very well because it has the concept of ports and a port is a number from an 8-bit number And you can use those for the data object IDs in the binary protocol variant So that's also quite easily Integratable and co-op is also quite useful Because we have similar requests like HTTP and co-op already so it can be directly mapped Now looking at the Zephyr integration. We have quite a few modules that we use There are two things that related modules one is the things at node library and that does all the parsing and basically taking the data from RAM putting them into the JSON structures and so on and for that library. We are also using the Zet seaboard library from Nordic Which is also part of Zephyr Yeah, and We have developed and things at SDK Which integrates all the communication interfaces you need like the Bluetooth part the serial part the canvas part It also integrates with the EEPROM and the NVM Subsystems so that you can store your values and also use the same protocol or the same data format for all your settings in the device Yeah, and that's part of this SDK, which is also a Zephyr module and you can just use that and Yeah, you just need a few lines of code and then you're almost done and that's what I'm going to show now Yeah, so the used features from Zephyr are iterable sections if you haven't heard about them that's You can specify special structs inside anywhere in your code and the linker picks them up and Puts all the structs of the same type in a row into memory So you basically get an array which you can define Anywhere in your code and we are using that to define all the data items and objects anywhere in the code So you can define them where you need them and then they are all put together into the database so to say Then we're using work use some peripheral drivers as already mentioned Bluetooth the shell subsystem and you can also use Thinkset as a custom logger back end so you can send the log data out to your cloud if you have that connection already Okay for the demo I'm using a Nordic Nrf 52840 Development kit that you can see here. The original one is here on my desk we're using Zephyr version 3.4 with a Bosch BME 180 sensor that's an environmental sensor it sends temperatures humidity pressure and some air quality value and We will use things that up which is also open source But not yet published in the app store, but you can download it on our repository Which is written in flutter. So that's also cross-platform. You can run it on linux system on Windows systems on iOS and I'm using Android at the moment Okay, let's switch to the code so that you don't see me typing all the time I prepared some git commits in a row Which I will leverage But we start with the simple hello world program. So on the right we can see the terminal I press hello world I press reset and that's the hello world. So that's our starting point now Let's integrate the sensor so I will cherry pick this commit and It adds a board overlay file With the Bosch BME 680 yeah, device tree nodes and Yeah, we were lucky that this sensor was already supported by Zephyr as many sensors are so the programming effort was not too much Everything we have to do here is integrate the Yeah, include the the sensor driver Then we are specifying to floating point variables the room temperature and the humidity that are read from the sensor We get the device tree Driver the Yeah, the device struct pointer Here that's all something you probably know and Yeah, here we check if the sensor is ready and in our endless loop we just sample the sensor and Convert the sensor samples into floating point variables, which are specified here and Stop for five seconds and repeat So let's flash this Okay, there we go. We have our sensor and it's now just printing to our console So that's usually the the first simple test But now we want to make the data more useful include the units and use things that instead So what we are going to do now us are well the the next step would actually be We want to have a controller in the end and not just have Measurements printed, but we also want to do an action based on the actual measurements. So we include implement a really simple control loop Which is here. So we have another function It's called run controller and it checks if the room temperature is below a target temperature the target temperature is now hard-coded to 222 degree C and We also introduce another variable that is heater on a Boolean and we add an LED So that's currently our actuator Which is switched on based on the target temperature and the room temperature We initialize also the the GPIO driver and here at the end we run the controller and afterwards sleep nothing else has changed so far and Of course, we had to add all these functions into our project configuration I'm using picolip see with the floating points so that we get the floating point point printed out here We include I squared C and the sensor drivers and we use log mode minimal because then With this tiny screen we can easily more easily see the log messages. So let's flash that and meanwhile We can go into the west yaml for a second Okay, now we see that the heater is on We have another value and I could now test the controller by hard coding that value and changing it But that wouldn't really make sense. We want to have some interaction with the device So that's where we are going to introduce things that and how do we get it into our code base? It's just this piece of Change in the west yaml. So we pull in the Zephyr the things that Zephyr SDK Which itself includes the things that node which does the parsing module So we have to put import true and it will also include C's ZC bore Yeah, so that we have everything we need Now let's go to the next step and Enable the things that SDK and define some objects Jerry pick this one Now we have some more changes First of all, let's look into the project configuration So now we enable the things that node library the parsing library That's this one and we enabled the SDK and we select a node name So each of the things that nodes I didn't really cover that yet Has a unique ID and it can have a yeah more human-friendly node name. That's optional And they have a special name in their structure. That's all specified in the specification Yeah, and it can be used to pass the data afterwards Now we also enable the con the things that serial interface Which is one of the subsystems all of the many interfaces we can enable from the SDK and then it will automatically use the serial interface here and The exciting part is now happening in our application So here we include the things that dot H and the things that SDK dot H and then we start defining our Database so to say and it's yeah really quite simple. There are some macros They are all documented in our on the the website if you browse to the code repository Then you get the rendered documentation and you will see what these parameters mean so basically these have this Iterable section feature behind them and we have to Yeah configure some parameters to build up this tree like structure and that's done by Numbers and these numbers are also the numbers used for the binary encoding if you use the ID based approach for very tiny packages You would probably in an actual application have a centralized H dot h file where you Define your number space here. I did it just here and Yeah, let's also documented which numbers are reserved by the SDK and by the node library Here we just take some numbers So this would be the sensor group and then we have the sub items temperature and humidity and the same for the control group So we use the things that add group macro. You could place it anywhere in the code really So it doesn't matter it would be picked up automatically by the linker and put in the right place Then we use the root ID. So that's the parent ID of This data object then we use the ID of this object itself it has to be passed here then the name obviously and This group should not have any callback assigned We'll come through that later Then we define two floating point items They have one more parameter or two more parameters So these are the same so parent group ID of the this float item itself then the name with the unit separated with an underscore and then we have the pointer to the floating point variable in RAM and Here we can define the number of significant digits we want to display because in Jason you can have infinite number of digits and if you Print floating points. Sometimes. Yeah, you you have huge number of digits behind the comma. You don't really need So that's specified here and we can specify some access rights So this one should be Readable by anyone and these macros are also part of the library which and documented there And an important aspect here is that this should be part of the subset for live reporting Yeah, same for all the other items basically all the same The only interesting thing is here Which is our stored? Target temperature and here we don't have the read only anymore But we set the right flag as well. So that means this item can be written to by the protocol Yeah, and now let's flash that Okay reset Okay, now we see we can see the reporting in the things that format and we could also send request via the serial interface now But sending direct request via serial interface is not ideal. So let's take the serial the Things that the shell of Zephyr instead So this is already integrated the only thing we have to do is replace the serial What we had activated before with the things that shell and in this case We are disabling the reporting because otherwise it will spam our shell all the time so now All right, so now we have a log message as well and we can use the things that protocol So it's the it's called things that the command here in this in the shell And then we can do this the question mark and we get all the data behind the root object And we can also do control For example and get the data behind the control. We can also set values and so on But I'm running out a little out of time a little bit and want to answer some questions. So I'll go a bit quicker The next one would be Currently we don't have enabled the storage backend yet So if we were to store the value or send the right to the stored value It would not be stored in in non-volatile memory actually So we have to enable that and that's also just a matter of a few lines of Kconfig What was changed in the main IA we had To add this to the non-volatile memory Subset as well and then it would be stored and the last part Would be to add the Bluetooth interface and That's really just a change in the Kconfig. So we enable Bluetooth Set the Bluetooth device name and we increase the package size as much as we can in Bluetooth and enable things set BLE here So yeah, and I'll go to the last no, I'll check out this one and So this is also all online already and you can have a look at it and into further detail Now let's see Okay, there we go. It puts out some logging information related to Bluetooth and Now I will open my phone and Get into this one So this is a screen copy of my phone and this is the things that app also open source Which can be used to interact with the protocol via Bluetooth now and we go here and scan for Bluetooth devices Here we got the Zephyr developer summit sensor, then we click at it and then we can access the device and now we can compare with our database we just defined so this is the database and This is the user interface that was completely automatically generated by the Bluetooth application and you see It takes the units generates a user interface out of it and now we can even configure the Publications here we can enable and disable them and we can go into the live Screen and see our sensor data So I can increase the temperature a little bit and then this should also go up All right. Yeah, that's it. We have time for one or two questions. I guess Yeah, Carlos Thanks for the demo really cool and the in general everything looks, you know, really sound to me So inevitable question why separate module why not upstream this in the main to the main repo? Well, we could think about that in the future. No problem with that Currently, it's it was still quite a bit in the flow because I almost rewrote the entire library because of some issues and Some legacy issues, but I would be more than happy to upstream it in the future Just I think this could this could fit pretty well into our subsist management folder where we have similar systems where you know You can manage the device sort of, you know, which in the end it's a data model So I think it could be a good fit. Of course, you know, there's details to be but Why not, you know, yeah, yeah, yeah for sure. Yeah, it also supports device firmware upgrade and things So it's it's really easy to just add some items you need and transfer data as you like and so yeah, okay. Thank you Thanks. Yeah Okay. Yeah Yeah, I think we have to stop. Can we have one question? Okay. Yeah, so just Yeah, getting contact with me. I will be around somewhere here and Otherwise, there's also my Contact details here Um