 And I'm going to explain less than the first one, because the first one was really explained and things like that, but today is more about using and how to do that and things like that, okay? But feel free to ask and let me just be prepared here. We are going to start. Hi, everyone, my name is Rodrigo. I'm here to explain a little more about ZBUS. We had a previous presentation the first day of the conference. And today we are going to talk about details of the implementation. We have a kind of example proposal here to work on to guide us, right? So let's start. A quick overview about ZBUS just to make a review of it. We have two kind of, we make threads talk each other using the buzz, right? So we have two kind of observers. We have asynchronous and synchronous. The asynchronous we call subscribers and the synchronous ones we call listeners, right? So when you need to make them talk, maybe you use a subscriber for something and a listener for other kind of things, right? But everything is based on channels and when we want to send a message from a thread to another, we have to publish to a channel or notify the channel, right? By not to find the channel, I just send a notification without change, replace the message, in fact. So there are a much more information about it at the first presentation. Sorry to not repeat that again here to make more time to discuss about the code, okay? And all the topologies here are covered by the buzz, right? One to one, one to many and many to many. So we can do that, I could say easily, right? We can do that. And many times you can see scenarios where you can have threads talking to listeners, callbacks, right? And everything else. So we are going to use an example of use, right? It's a little different from the first presentation. We have some tweaks here. I called this a buzz diagram. So form, it seems to be a, I don't know what, a cute image, right? We have some colors. But the arrows means the mean, when you have a channel, an arrow from a channel to a square, a rectangle like this one, when the rectangle is gray, then it means that it's a thread, okay? When it's yellow, it's a listener, so it's a callback. And when you have an arrow coming from a thread to a channel, it implies that we are publishing that channel, that thread is publishing to that channel. When we have our arrow from a channel to a thread, we are implying with it that the thread is observing the channel. The thread or the listener, okay? The thread or the subscriber effect, okay? And I usually use the same color for channels, but this one at the first, at the very end of the presentation, we are going to tweak it and make some advanced things over it. So we change it a little. And the dashed arrow implies that we are only reading a channel, not observing that. So we are not reacting to change on that channel. We just access or read that, okay? And a thread can read, so all threads can read a channel, right? And publish to a channel as well, okay? So step one, we are going to get this one at the final, I hope we have time to that, but at first we need to restart, right? And just to make sure everybody's seen this, okay? Is that good, the size? Okay, and this is the really, really basic implementation. It's almost playing real world, but here we have Zbuzz Include and I'm just gonna reuse the Zbuzz module, right? I'm not registering another module, login module, I am reusing the Zbuzz. Just this, and if we go here and the confile, we just have log and Zbuzz, okay? The basic log with log minimal and Zbuzz in some level, log in some level. I usually use the assert enabled because it gives me some idea during the program process that ever can happen before. As soon as they appear, it's better for me, right? So it's a kind of offensive versus defensive programming. So I like offensive in this kind of, offensive is weird word, right? But in this, I'm telling this because Zbuzz can help you sometime. For example, in Zbuzz, this is Zbuzz code, right? This is really short, in fact, and thin. But here we have some asserts telling you that you could not do this kind of thing. So if you have assert enabled, you could see that Zbuzz cannot run inside ISRs, right? So you cannot call Zbuzz functions inside ISRs because we use mutex and mutex are not made for what inside ISRs, right? So we kind of inherited this kind of limitation, right? So if you pass a channel or a message invalid, you would be warning or whenever we appear, okay? Is that good? So this is the basic, nothing so near here. I'm going to just compile and run that. I'm not going to, well, the boot banner and hello world as expected. And I'm not going to in details and tap things. I'm just gonna follow some commits I have and we can see what happens, what changes between them, okay? And that's it for this step one. And we have the, oh, sorry, the main thread. I've showed to you this one and we have just the Zbuzz enabled, empty, no channels, nothing, okay? And if we go, if we try to go to the step two, okay? Step two, and if we see here, I usually add some channel or something like that to gather information about the project. I don't know, the version, the firmware version, the hardware version, I don't know, some serial number, everything related to the project and the application at all, right? I don't know if you do that, I think so, but maybe some of you do that in a separated way or I don't know, I'm here just showing you a way to share data among threads on the system without even publishing to that, right? But it's just an idea and how we do that, right? So let me try to compile that first to just to avoid some issues there, okay? And here we have, let's try to start with the project info I add, I add a fire here called project info is not the best name we are going to change that in the future. I'll show you how to do that and change the name of a channel, it's really pretty easy in fact. And here we have a channel declaration, it's a definition in fact, we create the channel with a name, I usually use a suffix, a suffix, and if you prefer prefix, there's no limitations on names, but channels like I usually put a chan in the end, message, a message, and everything I use kind of this suffix. So this is the message, so we define a struct, it's not defined here, it's defined here, it's that struct is going to be used as a message inside that channel, right? So as you can see here, everything's constant, so to avoid accidental, in fact, you can change that in memory, but it's just, I don't know, I'm used to do that. When I, when I would not change something, I put constant there, and I think it's a good way to do it. And see we have firmware version, we have hardware version, we have serial number and model, right? And how can I change that? During the project declaration, we have, this is not correct here, this is the opposite, if you go here and see the, this is a really tricky macro, okay, inside, Zbuzz is really simple, the .c5 is really simple, but the .h has some tricky things, the channel defined is one of them, but you just need to take a look at the first line here, and the sequence, we have the name, the type, the validator message, oh, sorry, the validator function, this validator function can be used to indicate that the message you are going to publish to the channel is valid. So sometimes we would like to do that, and we can do that using a function that you can pass during the channel definition, right? And you can do different kind of things using that, because it's almost a pre-run function, right? So you can add some different kind of things and even you are not checking the message at all, but you are running something before the publishing, and if that function returns false, the publication will not happen, it returns an error, okay? So it can help you to extend some thing on the bus, on the channel, in fact, because you can do that individually per channel, so you can change the behavior of that, change some other things as well. User data is one of them. You can add some metadata for your application on channel per channel as well, so you can add a more thing there. So the bus was designed to be super really, really short, really fast, and you can build something on top of it, right? So we did that. Then we have the observer list, and you can have subscribers and listeners inside that list, and we have the initial value of the message. It's kind of weird because we have the message definition, the second position, but the initialization at the end, but we have some reasons for that, but not the case right now, okay? And here we have a new validator. We don't have validators, we don't need user data, so we don't have observers for that, so if you change that, nobody cares, it cares in fact, and we have the initialization of that message. So we have the firmware version, hardware version, zero number, and the name of the project, ZbuzzIUT1, a powerful one, yeah? In this way, we are defining that, and we are not able to change that. So we have a constant or really only channel for example, just to express you that we can do this kind of thing. So if you want to do that thread safe, we are going to do that in the end, we can change that thread safe as well and doing some different kind of things, but let's talk more during the presentation. And if you have questions during the presentation, please go ahead, interrupt. I would like you to have that more as a conversation than as a session in fact, okay? So feel free to do that, but if you have so many questions, we cannot go up to the end, okay? But let's go. And the project info we already have, have seen nothing changing here. The CMake, I just added that line here because I usually separate include and source, okay? And this code, I would like you to add that to Zephyr code as a sample, right? I will try to do that and I'll publish after the event. And I don't know the name I will use. Usually nowadays I'm using ZDS Z bus tutorial for now, but I don't know if it's a good name to put on the repository, but let's see what happens. But if you'd like to have that code, you can go to my GitHub, and there you can see a branch with this name, okay? So let's get back. Just remember we are doing this kind of thing here and the main function is accessing the data for the channel. Here we have a declaration of a channel. If you define the channel in an external file, you need to declare that inside the file you want to use, right? It's really similar to log, for example. So if you have that defined outside, you have to declare it here and you just need to put the name here. So Z bus channel declare and you can use the channel in this file, right? So here we are accessing the message directly, which is danger because it's not thread safe, right? If some thread goes there and published to the channel during the reading, maybe something goes wrong, but as I am developing the project and there's just one thread, the main thread, it's not unsafe, right? But take a look at that and we are going to improve that in the end of the presentation. And I am accessing that as I am taking the message, it's as a const message. So const, const, const, const, const. So, and we just have a pointer for that struct that we are pointing here, right? Remember, we have a pointer for that struct. So when we want to use that, we just need to use the fields of that. So this is what we are trying to do here. And if we run that, that's it, okay? We are reading the system information from a channel. So the channel is gathering those information there and maybe, depending on the logic you are doing, you can change some variables of it and that changes the system functionality. But we are going to talk more about it in the next steps. So step three, let's do that here before, for that step three. I tried to do that as dynamic as possible, but I need to go and get back. So the third step is to add the sensor thread, the start trigger channel, it's not a good name, but it's the name, and a sensor data channel. And here we are at the sensor thread and the mock listener. I call that mock because it's a mock effect. So it's almost, yeah. And here we add the main thread, the heartbeat of the system. So the main thread will publish to the start trigger to start the whole chain of reaction of the system, right? So every two seconds it will publish, in fact, it will not fire, I don't remember what I did, but you need to publish to the channel and sensor data will be notified and react, in fact, right? So let's see what happens here, what happened in the code. So let's start with the mock. So the mock's really simple. We have just the basics here and we have a callback function, right? The callback function has the channel pointer. So when you are a listener, you receive the channel that changed it to call you and call you the listener. So here we have the zebra's chain name, it's kind of metadata of the channel. And to have access to that, you need to enable this config because we've removed the name when it's not necessary to save memory, okay? So if you would like to have that, you can access the name of the channel and you can access the message directly and you can see the message size as well. This is another metadata of the channel. So here inside the listener, as we said before at the other presentation, the channel is already locked. So it's not necessary to lock again, you can access that directly, okay? Just get the metadata and the message, whatever you want. But it's const, so you cannot change that. You cannot change the message. Maybe you could do because it's C, C you can do everything, right? But you must not, right? Change that. And after all, we have a ZbuzzListener definition here. We are creating a listener here. It's the mock list. Again, I put a suffix here and that uses that function over there, okay? Over here. And this is it for the listener. And the senso thread, it's a little bit more sophisticated maybe. And here we have a subscriber definition. In fact, this is a subscriber notification queue definition. We have a queue for notifications that we define using this macro here. And we give that a name and this size of that. So how many notifications I can receive simultaneously, for example, if that subscriber is subscribed for, I don't know, four channels, maybe you can receive four notification at the same time. But if you manage this number wrongly, you can lost notifications, right? Because your queue will be full and it will not be visible, right? So we are going to talk more about subscribers. They are really sensitive, okay? And here is the sensor data channel definition, right? We have name, we have the same thing, the same error here, copy paste is not good when you do wrong, right? It's in the opposite position again. And we have an observer list empty. We have the initialization of that as zero, okay? And this is really, really a really simple implementation. I'm not mean to implement some external thing. I am doing this skeleton of the project here, right? But to simulate that, we have a sub-weight function here that locks the code here until the subscriber receives a message, oh, our notification, sorry. And after that, we generate the, we, I don't know, we generate the sensor data and publish that to the sensor data channel, okay? Makes sense? It's really simple just to show you how to do that. And we define our thread as usual. The only difference from this thread to another is that we are using the Zbuzz subscriber as a lock mechanism, right? Okay, and if we go to the, if we need to know which kind of messages, this one we can go here and see that usually I like to put all the messages concentrated in one single file to make that easy to import and to include in other modules. But you can do that separated as I am doing here. For example, I have project info and message separated. It's up to you, no problem. You can do that as, I don't know, as the way you want, okay? So the mock didn't change. The main function changed. Let me see here. Yeah, here, let's talk about the main function first because we are just remembering, we have the mock here and we have some new channels, right? We need to make subscribers and make things connected in fact. So here we have this dot trigger channel. It's really simple. We cannot, we cannot, sadly define a channel without a message. So you can use whatever you want. But if you are using the channel just to send events, you need to put something there. I usually put some Boolean thing or the minimal possible, right? I don't know, one byte. And I don't know if it's changing, in fact, the size of the struct because the struct's really big, in fact. Big, sorry. And anything change here up to that point. We have the channel, the Z bus channel add observer. Here we are adding dynamically, right? During the code is not during the compile time. We add mock listener to the sensor data channel. So we can do that and we can add or remove that easily. Just call add or remove and you can add or remove a listener from a channel, okay? So it's really that. And this one is the hard bit I told you. It's really simple. We are just not fine. The difference between the publishing, the pub and the notify functions are that the notify don't replace the message. In fact, it just executes the event dispatcher logic, okay? It's just that. So if you want to notify a channel or publish the effect and the only difference is that you change or not the message. And you slip two seconds and then finish, okay? So, but if you want to do that, if you want to do dynamic additional removal from the channel and the observers, you need to add another config. Everything is configurable because you need to be short and thin, right? So here's the size of that. It's a kind of pull, right? You can add up to eight dynamic listeners to some channels. So in that way, we are just using one, so it will fit no problem, right? If we go to here, we just use one, okay? But we have seven left. You need to take a look at that and maybe you don't need eight. Don't put eight there because you are going to waste memory, right? And that's it. So let's see what happens here during the code. Let's compile. So we add something there. Every two seconds, the main channel is publishing to the sensor data, so the sensor data is happening. And the beauty of that is the mock is just get the name and print the data of the message, okay? The message in a broad way. So here we have one, ten, and a hundred, right? So we have two, twenty, two hundred. I'm not the X computer, right? X decimal computer, but I'm supposing it, right? But it seems to be working properly, okay? So we can see that. We have a mock to help us. We can do that printing something inside the code we are doing, but if we can do that on the mock, it's better because it would not be necessary to remove that after the... I just need to remove the mock, right? It's easier. Or if you prefer to debug that with a GDB or something like that, you can do, but this is a really simple way to do and really classic, right? Classical way to debug or this kind of thing. Okay? Is that okay for you? Sensitrant, okay. Sorry, sorry. Oh, this one? Yeah, this one is... When you try to publish something, the question is the line in 32 here where I am pointing, what's the reason I have a timeout here? This is because we use Mutex internally. So if you try to get a channel that's locked, you need to wait. We could wait for that, right? If you don't need to wait, you can put kind of wait and kind of wait and that's okay, okay? Yes, yes. Yeah, it's almost the same as you can have in Message Queue and other kind of things on Zephyr, okay? I tried to make the things most... I don't know, most close from what exists, right? And I used the claim finish from the ring buffer, for example. I used that format here from Message Queue and everything I tried to make familiar for who already developed in Zephyr. Is that okay? Sorry, this is a relative line, sorry. Right below the include Zephyr.Edge. Yeah, exactly. What's number four over there for? This is the size of the queue, the notification queue you need on that subscriber because internally we have a Message Queue, so if you have, I don't know, if you have a really well-defined behavior and you don't need to worry about many publications at the same time, I don't know, a small number will fit. But if you have a lot of publications at the same channel you are listening or several channels you are listening, maybe increasing the number would be better, right? Go ahead. The subscribers that you are registering there or finding there, this is waiting for any message on the channel. Yes? This one? Yeah, this one. Yeah, this is, if we go... No, no, no, no. Yeah. In this file we don't have any definition about that, but if we go to here, we have... Oh, sorry. No, here. The Sensitrad subscriber is an observer of the Start Trigger Channel. So here we are doing the link, but this is a static way to do, right? That one here is the dynamic way to do. So one is compile time, another is run time, okay? Sorry, I did not show this part of the code. Thank you. Any question? Oh, here. No problem, I can repeat. So remove what part do you want to remove here? Sensitrad subscriber. Yeah, here, this one here? No, if you define it here, it's static, you cannot remove it, just you can disable that. There is a function that you can call to disable that, but you cannot remove, in fact, you will have that allocating memory up to the end, right? But it's up to you, and sometimes you need to do that statically or sometimes dynamically, and it's really up to the, based on the project needs, right? So go ahead. I saw in Kconfig there was that 8 subscriber definition, yeah? Yeah, the compile, yeah. Is that globally then for all channels? Is it not a definition per channel? No, sorry. This number 8 is for every subscriber, or sorry, every observer, because you can reuse that pool and put the subscribers and observers on the channels. So you just have one pool for... It's a pool. It's a pool, yeah, yeah. Okay? Okay, let's keep going. And this is the current state of the system. Right now, let me just check the time, okay? It's good. And now we are going to add the core thread. This one is observing the sensor data, and when it receives some notification from the sensor data, it will make some math or, I don't know, calculation, algorithm, something like that, and publish that to the payload. Again, I am using the mock to see if everything is working properly, okay? And I am just adding this one, this thread and this channel and put the mock as a listener of the payload channel. Okay, let's see what happens now. Let me try to go here. We are in step four, okay? Step four, step four. Great. So the core thread. The core thread is almost the same as we have on the sensor thread. This is really a standard way to do, right? We have a channel. We have the message we are going to use, we are going to publish, and here there is a bit difference from the other code. If you take a look here, for example, we are running a loop while you're true, right? And there we are just running this way. If you are using the arguments correctly, this function always will respond zero, right? But if you don't trust that, go check the error code and do the job. But no problem. This is another way to do it. And in this case here, the core thread has a... We defined a subscriber here, a subscriber notification queue, right? With four. I use... Usually I use four, but I don't know why. It's just... I don't know. I like const, right? All the time I use that. And for this scenario here, we have a core thread subscriber waiting for notifications, and we will receive notifications for the sensor channel. We are going to... I don't know if you remember, but core thread will receive notifications from the sensor data channel. So when it happens, we can read the message, right? So in this way, as we are in a different thread, we need to read that. In our listener, you just need... You can access that directly. But in a subscriber, you need to read. Because the read locks and unlocks the channel. So it needs to do that to be thread safe. And in this way, we can collect the S data from the channel, right? We are reading the channel. So when we read the channel, we get the message, and the message is here. I point to a script and get that as we do for message queues, right? And then we can do a complex math here to make the payload, right? Just to illustrate that and then publish to the payload channel. This is the way we are doing here. Almost the same here. The same thread, creation and everything else, okay? And what else changes here? If we can go to the main... Here, we are adding the core thread subscriber as an observer of sensor thread, right? We are making that connection. Okay? Rodrigo, why we don't do that statically? You can do, but I'm just doing different ways to show you. And we are adding as well the mock list to the payload channel. So in this way, we have the mock listening to sensor data and payload as well, right? So when the main thread makes the heartbeat start, the sensor thread will start and then publish to the sensor data. The sensor data will notify the core thread. The core thread will make the math and then publish to payload, okay? This is a chain of action. It's really even-driven, right? But it's message-driven. But okay. And then when it happens, the mock will receive the message as well. So it will be executed as well, right? And let's see. And let me see. Oh, nothing changed here. And everything's good, right? We saw the payload channel. It's defined here. So here's a different thing, but we have a 64-bit variable as a message in the payload channel. Again, empty and everything dynamic. Okay, let's see what happens when we compile that. So the things are running properly because we have sensor data and then we have payload channel. If we turn off the minimal from here, if we turn off that, we can see differently. So every two seconds, we have that, right? So the payload, the sensor data, and after that, the payload channel, it indicates us that it's working, okay? Payload, sensor data payload, sensor data payload, sensor data payload, right? Guys, is it low? Is it good? Can I keep the pace? Okay, oh, it's fast. Oh, it's okay, right? So everything is running properly. We can see that the sequence, everything is being okay here. So this scenario is okay. So we have the project info payload and everything running properly. So the last addition here is laura thread. It's almost the same thing that we did to car thread, but we have just a difference here that when the laura thread finished the submission of the package to the network or something like that, we published to the transmission done. Oh, sorry. The transmission done, yeah, the transmission done, and again, the transmission done goes to the mock, right? Just to keep track what we are doing and see if everything's okay. Good question. The mock's running on the publisher context. So if we have, oh, sorry, the question is, which thread is mock running, which context is mock running, right? When you publish to a channel, you run the event dispatcher logic. This is why we call VDD, virtual distribute event dispatcher. So every publisher must do that. So when, for example, when sensor thread published to sensor data, it sends a notification to core and runs the mock callback, right? When sensor data, when core gets in and published to payload, it runs the mock again, and every thread will run mock on that page. When you publish, you run the listeners. This is why it's not good to have plenty of them, right? It's good, it's fast and so on, but if you have a lot of listeners at the same channel, for example, you will increase the communication lateness of that channel, because you have to run a lot of listeners, okay? So what we are going to add the transmission done channel and the loader thread, and it's almost the same here. Let's check that. Okay, let me scan. We are in step five, right? Just from, make sure. So if we look at the loader thread here, it's almost everything is the same as before, but here we are simulating the transmission with a delay, just for make sure that it's working. And when it's done, we publish to the transmission done channel, right? It's really simple, really straightforward, and the only difference here is that we are using four as a priority, but it doesn't make any difference in this system the way we are doing right now, okay? They are not competing for something, so it doesn't make any difference in fact. And if we take, here we are talking about the Laura thread subscriber. We have the transmission done here again, a definition, here we have a Boolean, and we started with false, right? And here we have the transmission done, and we add Mochilis to transmission done as an observer. And if you take a look, we have just only four spare slots on the pool, right? That pool we are talking about, because we can reuse that. If you remove a listener from someone, some channel, you can reuse that because this number increases again. We can, the kind of recycling pool, right? So, yeah. So here we are adding that, and if we take a look here, we are doing almost the same thing, but we are calling Mochilis here as well. So everything is working. Let's see. So if you see here, we have sensor data, payload channel, transmission done after one second, right? Oh, there's no time here. Let's add that. So we have sensor data, payload channel, transmission done one second after. It's doing a good job, right? And you can do that automatically, right? You can write a kind of code that checks that for you. It's better. It's the best way to do it, right? You can write tasks inside the Moch to check if everything is running on the right sequence and the right timing and things like that. This is why we said that we increased the stability with Zbuzz because we can add code without changing anything to test if everything is running okay. So this is a way to do, and everything is sensor data, payload channel, transmission done, and it goes, goes, goes, okay? Great. So step five, we have the system we designed before. If you take a look here, we have all the channels, all the threads, everything almost the same, but we will start some tweaks, some things here. For example, the heartbeat is being done by the thread. We are going to remove that and add a timer, right? This is a heartbeat, two in two seconds. It's better to be done by a timer than a thread. I have a thread doing this. It's not necessary, in fact. So how can I do? We are in step six. Step six. Let's do step six. So here, we just change the main fact. If you take a look here, we are... I removed the code and add a work queue and a timer. Sadly, we cannot use just a timer yet because we cannot call, for example, Zimbuz channel five inside the ISR, right? Interrupt subs routine. It's not possible yet, but we are planning to do a kind of a sync way to call this kind of function that enables us to do that, and it will avoid this second step. But here, we have a timer, and then when the timer triggers, it calls this function, so we submit a work queue and the queue will not find the channel. So, we have a work queue there, so we don't need the main thread running anymore. So it returns zero as a good way to do, and we have two and two seconds, right? And it must run at the same as before. Again, I forgot to disable that since the beginning, and I'm not going to commit anything here. And, okay, two seconds. Yeah, sensor thread, payload, transmission done, and everything is working exactly as before, so I just need to change the main. Other threads are untouched, right? This is one of beauty of that. It's not necessary to change things when you are trying to change parts of the architecture, because everything is based on channels, not on other stuff or other code, right? Just channels, so modules and channels, and you can, if you think like that, you can make a firmer software, better software, better and quicker and better to maintain. But sometimes, if you have a really short MCU with a few k bytes of memory, maybe you cannot use a lot of threads there. So use listeners for that, and you have a lot of callbacks calling a kind of sequence. It would be good as well, okay? Now I'm planning to add some FSM examples using listeners, just listeners, to make a kind of state machine use listeners, and you can use events and callbacks to make a kind of execution, and after that I will do the same thing with behavior trees. I don't know if you have heard about it, but it's a really good thing to... It's a kind of competitor to state machines. I don't like to offend anyone that likes state machines, but behavior trees is fighting with state machinery and state machines are competing to that matter. And okay, so let's just remember here what we are doing. Okay, the timer, everything is okay, right? So if I would like to change the core from a thread to a listener, what to do, what happens with this system, right? Let's see. And if we just to take sure we are on step six, right? Oh, we are in step seven. I was talking to you and I forgot that. Okay, so step six, right? Step seven, sorry. So okay, and here if we... First I added a K-config, kind of config that core as a listener, just to make that as, I don't know, modular and dynamic as possible. So here, the default configuration is yes, right? So when you try to see what happens here, it's almost, sorry, it's not my core thread. So here I am going to add a line here. Sorry. What happened? I think, I just see, just... I think that the files got lost here. Let me open it again. Yes, I don't know. Let's see. And here we have a subscriber, we have that code at the subscriber, but I've added here a conditional compilation flag. I don't know my... Let me try to... Yeah, it's kidding me. No, yeah, yeah. It was working, working, but okay. And in this way, we are doing the job of a thread just using a listener, right? Here we have the listener definition. We use a callback and the name of it and the execution. It's almost the same as we had before, but internally here and the listener. But remember, inside the listener, we don't need to lock the channel, so we can access the message directly and get that without copying because when you read the message, you copy the message from there. It's quicker to access than copy, right? So inside the listeners, you can access directly and publish. But I don't know if you remember, but it was a question there. What happened when the sensor thread published to the sensor data? It will call the core thread and the... Oh, sorry. It will call the core thread and the mock listener, right? But inside the core thread, we have a publish as well. So it will chain the... You make kind of a chain of publishes. It's not bad, but you need to be aware of it because sometimes you may not understand what is happening there, but it's happened exactly that, okay? We are going to see what happens here, but in one way or another, we are running that during the publication process and everything is going on. And a good thing here is we need to add the core listener as an observer of the sensor data, right? So I usually use the sys init to do that because it runs before the main. Make sure that you are using the application flag here, please, because if you use pre-kernel or post-kernel, maybe it will not work, okay? Use application and you can use our priority here. We are going to see as an example when it interferes, okay? So let's run that to see what happens. Is there any change? Could you notice that? We have the payload before the sensor data here. Why is it? Because we need to have sensor data, then payload channel, then transmission done, okay? This is just the sequence of the printing on the mock. It's not wrong, right? It's happening as it needs to have to work, but the only thing here, the trick thing here is to know what is in a different sequence. Why? Sorry. Why is different? Anybody? Who calls call thread and mock? Sensor thread, publishing process, right? But who is attached or who observes first the sensor thread? Who is... We add... Who we add first, call thread or mock? If you take a look here, we are adding the core listener before the mock listener. This is why we see the print before for the... We can see first the core, then the mock, right? So in the sequence, we need to take care of it because the sequence of registration implies the sequence of execution of the listeners. If you have there on the channel definition, for example, if you go to main thread, for example here, if you have several observers here, the sequence is exactly this one. But first the listeners, then the observers, okay? So we have a lot of documentation on the Z-Buzz documentation, so you can see the sequence. We have the event dispatcher... Oh, sorry, event dispatcher logic running, everything there, okay? But this is really important. You need to know what's happening here because sometimes you are trying to find a bug or the fact where there's not a bug here, right? This is just a way to do it. We need to do that. We need to know how to do that. And we are going to do that properly in the next steps, okay? Is everything okay for you? Great. I think that we are in a good timing. Okay, and in this step, I would like you to simulate a kind of real-world scenario, right? For example, I have replaced the name. I did not like the project info. I put the app info to be more... I don't know. It's better, I guess. And in this scenario, I would like you to imagine, for example, I have a EEPROM with some of the information that my application needs to read to work, for example. I don't know, there we have some... I don't know, some variables or serial number or something there. But we need to write that channel to publish to that channel just once. It's not good to publish to that more than once, right? We are going to see that in more detail right now. So, this is step eight, right? Just to be sure. So, I developed that on the Zephyr code already. I'm going to try to add a sample with that. I think it's... Oh, sorry, I already forgot. I forgot the eight. So, okay. Just remember, we are changing the app info logic, okay? And we... If we... Here, app info. Okay, here, as you can see, we are using a validator function now, right? We need to use the definition of the validation here and define that here because it's a kind of psycho dependency here, right? Because we need the channel inside the validator function and we need the function inside the... This is why we did that, but it's not necessary in all the cases. Yes, maybe it's necessary, right? And in this way, we have the old app info thing and we have a validator and we have a user data called locked, right? We have a Boolean variable, a static Boolean variable where anyone can externally get that, right? This is why we use stacked static here. And inside the validator, just check that is locked or not? If it's locked, we answer false so that the channel cannot be published. If it's true, you cannot publish to the channel. If it's false, you can publish to the channel. So it's a kind of logic lock on top of the channel. It's not a real lock, but it's a logic one. So what we are trying to do here is to make that like this. Let me, for example, here... In this way, for example, we are pretending that we are fetching some data from external source, I don't know, a security chip or something like that. And then we want to change the channel metadata, right? So how do we do that on Zbus? We claim the channel, so the channel is locked, but it's just locked. You are just locking the channel here. So after that, you can change the information directly. So I am accessing the pointer there, but it's not constant in this case, right? I'm accessing the reference of the message, and I am changing that directly. It's not unsafe because we have already locked the channel. It's not a problem, it's the way to do it, right? So we are changing metadata, we are changing the message inside the channel. We are accessing that and changing that, right? So here, the app info, we change a little bit of that, and this is not constant for hardware, for example, we are collecting the hardware version from that external source, and the sample timeout. The sample timeout is the hard bit of the system, right? So as we are trying to emulate here, at that point we got the information, and we put the information here, and we have the information here, and after that, we set the lock to true, right? We set the lock to true, and then we finish the usage of the channel. So, is it safe to use app info here? What do you think? No. Anyone discord? Do you know if we discord? I don't know the right word in English. Discord is another thing, right? But here, it's safe because you have a lock here, and another thread cannot publish to that, because we did a lock there. It's a different kind of lock, right? But if you take a look here, if the value of the lock is true, no thread can change the channel, in fact. So we are kind of doing an almost constant channel, right? It's almost constant. If I want to change the channel, I need to claim the channel, change the lock, and then publish to the channel. This is a second level lock, so you can do whatever you want, right? This is a kind of situation we are trying to simulate here to show you the way we could use Zbuzz, right? Okay. So let me see if anything else changed. Oh, sorry. I guess it's it. Okay. And let's see the main function. I used the mouse here. So what happens with the main function? In fact, we changed that. The timer, we are using the app info sample timeout for that. So we collect that from external source and send and set the system heartbeat and everything is done. And here I'm trying to publish to app info just to make sure that is not going to publish, just to show you that I'm not lying, right? So let's try to compile and run that. So we have an error here. So the error was what I was expecting. So you cannot publish to that channel because it has a second level lock or something like that. Okay. So we still have the wrong sequence of printers here, but we are going to fix it in some moments. Okay. So the next step, fix execution sequence. How to fix that? How to fix that? Step nine, we are going to... Step nine, let me close that. Go to step nine. So I'm repeating to not forget. Step nine. Okay. So what should we do to avoid that? We need to initiate that before the core. We need to add that before the core one. So if you remember, we have a seasonate there on the core thread, right? But the priority is three. So I need you to add a seasonate here with priority, a higher priority. Not zero is too much, but if you use two, it was okay. But in this case, we are... I put everything here just to separate from the main fact. And here, we are putting the mock list as first on all the channels observer list, dynamic list. Because we have static list, we have dynamic list, right? So if we compile and run now, we have sensor data, payload channel, a transmission done, and everything is okay again, right? Just we need to know how to do to not, I don't know, when we don't understand what's happening, we maybe think it's a bug or it's a misbehavior or something like that. But in this way, we can figure out how to do and we can fix using the right thing. Okay. And the last step, we are going to make a loop. I said during my presentation that you must avoid loops on the bus diagram because if you take a look here, the core will publish to the payload, the Laura will publish to the transmission done and transmission done will... There is a loop here. When you have arrow crossing, sometimes it's a loop or you can see a loop explicitly there. I put that in orange just to say to you, but it's not necessarily orange. It doesn't need to be orange, right? But what I'm trying to do here is to retransmit the information. For example, if the Laura thread tried to send the information to the Internet and that goes wrong for us any reason, it will respond to the transmission done with false instead of true. So the core thread receives that and publishes again to the payload just to make sure to try again because Laura thread is not the one responsible for starting the communication, right? It's just respond to events, right? So core thread is responsible for that. So you must feedback core to that information. So when you do that, you can find some difficulty with a loop, but if you take care and if you know, if you are aware of it, you can use it in your favor, in fact. So in this situation here, let me go to again, step 10, right? The last one? We don't need to. So the last step here, we change the core thread a bit and here we have... Oh, sorry, I guess it's here. I don't know why it's this... Let me try here. Yeah, it's no, I don't know. It's just the ID, right? Okay, let's try that again with one here just to explain to you. Oh, sorry. It's not one, it's zero. So in this case, we have the core thread subscriber observing the Transmission Done channel as well, right? So we have that feedback. So here, what I am trying to do is, during the subscription, the wait for the notification, you have to check, because now you can receive notifications from two channels, right? You have to check that before. If it's sensor thread, you just need to publish the payload. But if it's transmission... else, right? It is from Transmission Done. You need to check that. If Transmission Done is true, do nothing. If it's false you have a retry, try it. And if you are out of attempts, forgot it, sorry, okay? And the retry is just a boolean. It's just a regular boolean variable. So here, if you run... Oh my God. Hmm, we have time yet. And here. Okay, okay, here. And I forced... I have forced to the LoraTrad to just... sorry. LoraTrad to always respond false. As I don't know, we have a defect. We have some problem with the network and it cannot send the message just to make sure we are having that thing correctly. So we have sensor data, payload channel and then Transmission Done. But the Transmission Done content is false. So retry that. We retry publishing again to the payload, right? But it's not necessary to publish. We just notify. Because we don't want to replace the message. We just want to notify again. We just need to notify if you recall here. We adjust. I'm using mouse for everything. Here. We just notify the channel, right? We notify to enforce that we need to try again. And it retry... Publish to the payload again. Payload channel again. Transmission Done again. But false again. We could not. We just gave up and print an error or save that in some kind of memory or send that to a... No, it's not possible because it's not working, right? So you have no answers from the device, right? So this is more or less what I would like to show to you. It's a lot more. We have a lot of samples at the repository. I don't know if you have tried that. We have a good documentation. If you need some help to use that, we have a Discord channel just for ZBUS. And we talk a lot there. And we talk a lot. Not so much, but we have great movement there. And some tips and tricks I would like to just remembering with you. With listeners, avoid the excessive views of them. If you have a lot of them in the channel, you will increase the latency of the communication, right? And it's sometimes if you are aware of that, no problem, but if you need performance, it will be not good. Do not sleep inside listeners, please. We are trying to do that fast if you sleep. It's a kind of scene, right? And we must treat listeners as ISRs. Must be quick. And if we have any heavy load or something like that, we could postpone with our work queue or something like that, but not process that inside the listener because it's running every time you publish something, right? Use ZBUS channel or course message inside the listener because you don't need to copy the message. You can access that directly. It's really better than this. Better than copy, right? It's really faster. So use that. Subscribers, take care with them because maybe you can face some losses or duplications. Losses, if you choose that number you asked the fourth number. If you have a notification queue too small to receive the notifications the subscriber will receive, you can lost notifications which is really bad, right? And if you produce more than the subscriber can read so the producer is pursuing really more than the consumer can consume you can see duplications because you have the notifications goes to the notifications go to the subscriber but the data the subscriber has to go there collected. So sometimes if you have a lot of notifications when you go there there is the same message you are reading multiple times. So if you need if your application cannot deal with lost duplications and things like that use listeners and if you need to keep the data sequentially and everything else use listeners in conjunction with message queues, okay? It's a good approach to solve many problems. And I have a PR submitted that is a kind of confirmed channel where you can like that lock I did for the sample here we have a sample that just enables threads to publish to a channel when the subscribers had read that. So it's a kind of delivery guarantees for subscriber. But I didn't put that inside the zebus code because because it would be too much and we we are trying to do the basics as thin as possible as fast as possible, right? So you can build a lot of things on top of zebus like a socket and a TCP for example you have socket, you have TCP over socket, right? It's almost like that you can do a lot of things. And take care with unsigned loops and think like I said before take care with unsigned loops and chained publishing loops because sometimes you have this kind of thing and it may be can generate some loops and if you are not aware of it maybe you can think it's a bug or something like that and I usually suggest people that use zebus to make the bus diagram. It's really interesting to keep that the things organized and kind of architect the project before coding. It's not sometimes people don't like to do this kind of thing but it's interesting because if we think in software as we are thinking in a house we have all the structure and all the planning before to construct, right? Because if you are going to build the house and during the process we discover something that we not thought we need to change and we will write something and lost a lot of time. So this is a kind of design. So we postpone everything that you need to run inside an ISR with a work queue or separated thread, right? And extra. We can use zebus as almost like a real-time database for example that info we had a kind of table there, right? And if we have different tables on channels we can think as a kind of proper system or real-time database because we have similar things as for example for a base or something like that, right? It's a really different approach but you can think almost like that. And a good idea is to isolate hardware code from the application logic use channels as well. So you can do that to make the code more reusable and decoupled from the hardware and reuse that more. And you can use the channel as interfaces as well, right? You can make a big module with a lot of channels where you can put data in and put data out and reuse every part of that on other code, okay? And that's it for the community that is watching us online. I am going to respond to the questions at Zimbus Discord Channel, okay? Sorry for the time and thank you. I don't know if you have any questions, additional questions here in local. Go ahead, we have two minutes. Thanks. So, what is the delay of one second? What happens if this delay is bigger than the timer at the beginning? So, will the queue just fill up and then... Yeah, in this case we will lost the notifications. This is a kind of design problem because if you have this kind of scenario, maybe your system heartbeat cannot be two seconds, you have to be aware of it because maybe sometimes you will produce more than consume and it's not good. Okay, and second question is Zimbus also available for long-term support means Cepheid 2.7? No. Sadly, no, the first version we started support Zimbus is 3.3 in fact. Sadly, I would do but it's a lot of work to make that work for the past and I'm just thinking in the future, sorry. Okay. There's another question there. Does Zimbus allow to create loops on the same channel? Basically, I publish a message on the same channel I'm listening to. Yeah, this is not a good idea, right? And now we enabled that but it's not a good way to do but one of the things that we are trying to do right now is to avoid this kind of behavior because you can avoid receive a notification from yourself, right? But it's possible, so I have to finish. Thank you very much for your time and questions and if you need to talk to me, I'm Ron and I'll be here until tomorrow, okay? Thank you.