 Hello, everybody. Thank you for joining this talk. My name is Alex and together with my colleague, Yana, we are going to walk you through a tutorial about how to use Rust on the micro bit version two to build a greenhouse controller. This is a completely live session, even though it's recorded. So any small problems will try to correct them on the way. That being said, thank you for joining Yana, you have the floor. Okay. Thank you, Alex. Hi, everybody. So in this tutorial, we said that we'll be using Rust to program the greenhouse. How will we be using Rust is a bit different because it won't be used at the application layer, we will integrate Rust as at the operating system layer. And this means that we will use a special operating system called talk. It is a new operating system. It is designed for embedded devices. And it as the quote here says it is designed to run multiple concurrent applications in a secure manner on microcontrollers that have very low capabilities. So a few details about talk. It is an open source operating system. It is a preemptive embedded operating system. So as I said, it runs multiple applications at once. It supports Cortex M and RISC-5 MCUs. And it also is one of the few embedded operating systems that uses the memory protection unit that implement this. The internal space and the user space are separate. So we have the kernel and then we have the applications. And this is a bit different as most the operating systems embedded the kernel and the user space. And it is capable to run different applications. The applications can be developed in programming languages such as C or Rust. The applications don't need to be fully secure because the security is guaranteed at the kernel level. The kernel and the drivers are written completely in Rust. Okay. Now to have a more clear image, a clearer image of the architecture we have here a picture at the lowest layer we have the hardware. On top of the hardware we have the drivers that the drivers that we know for that are regular drivers but they are written in Rust. And here we can also write some unsafe Rust code because it wouldn't be possible otherwise. On top of this we have the hardware interface layer. And through this we have a way of communicating between the drivers and the capsules. The capsules being over they are located over the on top of the hardware interface layer capsules are also sometimes they are similar to drivers but they are higher level drivers. These drivers consume the services that are provided by the lower level drivers. And this is the kernel. And then we have the applications. We have different applications running at the same time. We see we have some applications that are written in Rust, some that are written in C. Some applications may do some malicious stuff like we see that we have an application that has that while one. Okay. And for the hardware part we will use a micro bit. We have here some pictures of the micro bit and you will also see it a bit later. It is an educational device built by BBC but it also it is also quite powerful for more advanced stuff. The main advantage is that it has on hardware debugger integrated into it. We have those. You can see that we have there the NXP KL27Z. This is the debugger. And then we also have some peripherals like we have an LED matrix. We have several sensors buttons and all those stuff that we can use to build our main house. Okay. Now, we have the talk operating system, and we have talk loader. The talk loader is like an app store built especially for talk. It is a tool used to manage the talk applications, and we will use it to manage our talk applications. It uses TAB files. These are talk files, talk specific files, and we will discuss about this a bit later. This is Python and it is implemented for a couple of boards. It is still, it is working progress and it still needs implementation. So if you want to contribute to it, you are more than welcome. And we will use it to deploy the applications on our device. This is basically what it does. To be able to use the talk loader, we need a special boot loader and this is called talk boot loader. And we can see it in this picture at the lowest layer. It is called, we have the talk boot loader. So we first need to flash this talk boot loader on our device. This is how we will upload our firmware on the device. For this, we download the boot loader and then we run the comment make flash boot loader. We will see it in action. Once we have the talk boot loader, we can use talk loader to upload our kernel and our applications. And we can see that we have separately the kernel and the applications. For deploying the kernel, we can use the make flash command. This runs on top of open OCD. And what it does, it deletes all the applications on our device and it flashes the kernel and only the kernel. We can also use make program. This will deploy a bundle with the talk kernel and applications. This is a bit different. And then if we want to deploy only applications on their own, we have the talk loader install command. As for our project, we have here an example of a project structure that is built for two different devices. So this project structure can be used also for a micro bit and for a Raspberry Pi device, but we will focus on the micro bit part. At the lower layer, we have the kernel. So we have the folder talk where the kernel is located. Then on top of it, we have lip talk C. This is the C user space library that we can use. Then we have the folder that is specific to the board that we are going to use that contains all the board implementation. And in our case, we need the micro bit V2 because this is what we are going to use. And we have a drivers folder where we can develop our custom drivers. So this folder for now is empty. At the application layer, we have an example app. This is an application example, the simplest hello world application that we can run. And we can modify it and we can also add some other applications and deploy them on the device. And then we have drivers. These are the libraries that we can also implement for the drivers that we have. The drivers is right now empty. And if we create some drivers at the kernel level, then at the application level, we will also write a user space library for that driver. What's mentioning here, Ioanna, is that technically talk allows you to write applications in Rust as well, but talk 2.0 has just been released. And the Rust user space library is not yet comparable with this one. So for this tutorial, we will limit ourselves to writing applications in C, but most probably somewhere in the near future, users will be able to leverage the Rust user space library. A few words about applications. And if you're familiar with operating systems, this might be strange. So at the lower level, we have the kernel and above the kernel, we have the applications. One thing that is completely different from talk and other operating systems is that application each application has its own memory space. And due to the fact that talk provides memory protection applications are not able to escape this memory space. Now, in order to be able to detect a stack overflow error, the stack is placed on the bottom of the memory. So any usual applications on Linux, for instance, the stack would be on the top of the memory, and the heap would be below the stack. In case of talk to be able to detect a stack overflow, and due to the fact that microcontrollers do not have a memory complicated memory management unit that the stack has been moved has been moved to the bottom. On top of the stack we have the data. So that means global variables, and then we have the heap. An important thing is the grant. The grant is a special memory region that is that lives inside the applications memory, but is only accessible to the kernel. This is where capsules or drivers are able to allocate memory for each application. So talk, as you said earlier, does not bundle the applications inside the kernel so most embedded operating systems like Fiat toss or Zephyr will build a single file containing the kernel and the applications. This is not the case of talk. So talk has the separate kernel and has basically executable files. These executable files are not elf files like in Linux, but are TVF files or top binary formats. Consider them as simple application files executable files that contain the code and some additional headers that are needed by the kernel to be able to load the application. So TVF files can be bundled into a tab file or talk application bundle. And so this allows you distribute top applications, because at the time of compile, you have no idea on which platform on which microcontroller this application will run. The application is compiled completely separate from the kernel. Depending on the microcontroller that the application will run, the architecture might be different. So within a talk application bundle, we have several TVF files, so several executable files, usually for the arm architectures and for the risk five architectures together with these TVF files. We still have a manifest file that provides information about the bundle. This is very similar to what Apple does with IPA files that run on different iPhone platforms. If we take a closer look to the TVF file or the talk executable file, it is composed out of three components. One is the TVF header. And these are some headers that the kernel uses to load the application, the actual application called so text and data. And then we have some padding that is necessary to obey the memory protection unit rules. So this padding is not exactly padding to four bytes, but it's rather a padding to the power of two, usually for the Cortex-M. For the risk five, this is not so restrictive. So this padding makes it possible to separate applications and run them in separate memory protection zones. So in this case, the header is actually formed out of a TVF base header, which is displayed here, which has the version, the header size and the checksum and a few flags, and then followed by several TLV elements, which are typed length and value elements. These TLV elements give you the application name, the amount of memory that the application needs, the stack size, so on and so forth. This TVF files a little bit later using talkloader. Thank you for bearing with us with this presentation. And now let's get to the demo. I will switch here to my Visual Studio code. And you can see I have here the exact project structure that Jana has described earlier. We have here talk, which is a link to the talk 2.0 release and LibTalk C, which is a link to the C library, which is version 2.0. It might not be the newest version of talk, so at the time of the presentation, most probably talk will have some commits after the 2.0 release, but this is a stable release and we will use this for this example. We have the kernel folder and the applications folder. And what we forgot to mention is how to get this folder structure. So if you go on GitHub, and we will provide the link in the description while it is embedded IoT. We have here the talk project repository, which is a very good template that you can use in building your own talk project. What you need to do is to go to this link and say use this template and GitHub will already try to make a new repository from you out of this template. Then you just have to clone this repository and you're good to go. Okay, I'll just close this one and go back to my repository. Okay, so first of all, let's try to compile it. So we will go to kernel drivers and go to microbit v2. So this is basically what we will do in the console. And in order to compile talk, we will simply run make. Please make sure that you have rust and make installed. All the other tools will be installed when you run make. Don't worry about the rust version. Again, make will update it for you. Usually compilation takes more but we already pre-compiled this version of talk for microbit. And it says it has about 126 kilobytes and this is the kernel. Okay, so first of all, let's upload it to the microbit. And for that, we will run make flash bootloader. This will download the bootloader for fraud. That should be this will download the bootloader and flash it to the device. In order to use this command, you need open OCD open OCD. And this will need be needed to directly access the board. Now, if everything works fine, I will take a look at my microbit. And what I need to do if you look at the microbit is press the a button and hold it. And I don't know if this is visible. So I'm pressing the a button here. And at the same time, I'm pressing reset. What you can see here is that the LED for the microphone has been lit up. And this means that the board is in bootloader mode. Now, if you really use talk loader, we can ask some information about the board. This is the bootloader info. And it's reading. So what we can actually see is that the microbit has been contacted. It works. It has no applications installed, and it has no attributes installed. If this does not work for you, because some versions of open OCD don't allow you to communicate very well with the board. So what you can do is press again, bottom A, reset the board to place it into bootloader mode. You can see the LED blinking, not blinking, lit up. And I will just try to talk loader info and minus, minus serial. This will make sure that talk loader will use talk bootloader and not different methods to communicate with the board. And it will ask us, which is the USB port? And this is my USB port. It needs to be something with BBC. So we're pressing for its reading. And as you can see, it is able to give us more information. So we have a microbit V2 board. It's a Cortex M4 processor. This is the bootloader hash, and it has absolutely no applications on the board. Okay, so let's flash the kernel for that. We will make program minus, minus serial. Once again, the minus, minus serial is necessary to be perfectly compatible with what you have. If it works without minus, minus serial, just go ahead and use it. So once again, press A, press the reset on the back. You can see the LED. And let's go. Oh, something. Oh, sorry, minus, minus serial. Sorry, this is not talk loader directly. So just make program should be fine. Okay, it's compiling. And in a few seconds, we should be able to see it on the board. It's flashing. Okay, my board is flashing here. Now that we have run these two commands, we should be able to see talk running on the board. We will open a new terminal here, and we will say talk loader. Listen, this will open a serial console to the board, and we'll give us access to it. Again, it asks us about the serial port. So we will select the BBC. And just to make sure that talk runs, I will simply reset my board and press the reset button on the back. There you go. And as you can see, we have any civilization complete entering main loop. Now, even though that this is not really visible, talk has a console where you can run commands. So if we write hell, we can see it says welcome to the process console. Valid commands are help status list stop start fault process and kernel. Okay, so status will give us the status of the operating system at this moment. It has zero active processes, no time slice expirations. If we write list, it will show us a list of processes that run, but we have no process running yet. And if you write kernel, we can see the version of the kernel. So we can see we have top 2.0. This is the memory region occupied by the kernel. So everything seems to be okay. Good. So we'll just leave talk loader running. That's okay. And let's upload a simple application that will write Hello world for this, we will exit this folder. And we will enter the applications folder and use example app. So let's open it. And this is a simple C file. It should be super comfortable with it. It has STD IO, it prints Hello world and uses some driver action because we have no driver, no custom driver will simply delete these ones and just leave Hello world. Once again, because we have no external drivers, we will also comment external libraries in the make five. All that we need to do is to make this will compile. The first compilation takes a bit longer because it compiles the libc library so not the libc, the lib talk c library so the whole user space library for talk. And it actually compiles it four times. It compiles it once for Cortex M zero and three and four and seven. So we won't have to wait a little bit. Don't worry. This is done only once. The second time you'll compile an app. This will work flawlessly and immediately. Okay, so we'll just wait a little bit. Okay. Now, as you can see, it has built for four iterations of our application for Cortex M zero and three and four and seven. Okay. Now, the tab file is located into the build folder. And we can see example app tab. Okay, so if you run talk loader inspect tab inspect tab. If we search all the tab files in the current folder and it's sub folders, it will find our example app tab, and we'll say hey this is the build date. This is the minimum kernel version that we need. This is 2.0. This is the size of these architectures. And if we want further details, we can select one architecture and it will give us several details about the application, because the, the micro bit is a Cortex M four. We'll simply use two. And as you can see, I will say, this is version door to off the TVF. This is the size of the header. It's called it's not sticky. That means it can be deleted easily from the board. It needs at least four kilobytes of RAM, almost four kilobytes, it's called example app, and it needs the kernel 2.0. Now let's load it. We just hit talk loader install. And first of all, we'll take the board. Press the A button and press the reset button on the back. Again, the microphone should be on. And we'll say talk loader install install minus minus serial to make sure it's compatible. And again, it asks us which is the serial port or say for and it starts installing the application on the board. Let's see what happens from time to time. The board jumps out of loader mode. So make sure your LED is still on. If not, press the A button and again press reset on the back. As you can see, it started flashing and it should be on. Now if we go to the second terminal. Let's reset the board one more time. We can see it displayed hello wall. If you run list. We can see we have one application which is called hello world. It has speed number zero. It performed 13 system calls. It's yielded right now. So basically stopped it waits for events. And yeah, we used it. If we write status, we can see we have one active process and none of them have expired their time slices. If we write process example app. We can get information about the process is memory. And we can see it used 240 kilobytes of its sorry bytes of its 2k stack. It uses 500 bytes of data. Its heap is about 1.5 kilo and its grand space of the kernel has allocated one kilobyte in its grand region. So, more or less, this is what it does. Okay, we can stop the process example. Now it stopped. So if you run list, it should be stopped. And we should be able to restart example, but nothing happens because I mean we stopped it. We started it again, but it can't continue because it just wrote the printf and that's it. Okay, now let's install another app on the device. And let's go. Actually, you know, let's, let's make it do something different and let's blink a lead. So we include LED dot H. And let's say, not instead of Hello World will say we have presented the LEDs. Let's see like this in LEDs. The count. Hope it's like that LEDs. This will basically tell us the number of LEDs available. And this should be all so many LEDs we have will run make once again. And top loader install for my board jumped out of food loader again. So flash is the app. If we reset the board one more time, we have 25 LEDs, which is actually correct because we have an LED matrix. I'm not sure I can make this visible here. Okay, so, um, let's again list processes. It stopped it performed 14 system calls and it stopped. Let's blink one of the LEDs will say why. Let's see it will be one one. Let's say, um, let on for led number zero, uh, delay a few milliseconds, say 500 milliseconds, let off off and delay another 500 milliseconds for delay we need to include the timer. Okay. Let's again compile the application and install it again. It's installing and it actually works. So if you can see here, the application is yielded, but if we continuously run this, we can see it performs system calls. If you look at the board, you can see the LED is flashing. If we stop the application, say, let's stop the sample app. You can see the LED is not flashing anymore. If we start the app, you can actually see the LED continues to flash. Okay, so, um, this is the applications in talk. Now, let's try to write some rust code and see how we can perform and write a simple driver and then go towards our greenhouse. We'll go into the micro bit. Sorry, into the kernel and into drivers. Now into drivers, we have one single library, one single file, which is called deep RS. And this allows us to export other files. We will add here a driver greenhouse. RS. And we will say, hey, public module greenhouse stock RS. Sorry. Greenhouse. Okay, so let's write a driver. Now drivers are located in talk capsules, SRC. And here you can see several drivers will take the easiest one, which is LED. It has some documentation, and it has some kernel imports. Okay, so we'll take a few of the imports here and place them into our driver. So we need like this, we need command, we need kernel Cisco, because we need to implement system calls. We need command return and system called driver, Cisco driver from their error code and process ID from the kernel. Now to write the driver, we have to define a structure. greenhouse, which is completely empty. It's an implementation that is for now empty greenhouse. Of course, it needs to be public structure. Right. And in order to write a driver that exports commands and system calls to the user space. We need to implement Cisco driver. So our structure needs to implement this. So what we will do is right implement Cisco driver for greenhouse. Let's see if we can jump code, but we can't. So we will go into the kernel and take a look at the Cisco driver trait. We'll go to talk. Colonel. SRC. Sorry, platform. Cisco driver. Here we go. Okay, and we will say trade Cisco driver. So this is the kernel Cisco driver trait. So stock supports three types of system calls that can be exported to the user space command, which asks the kernel to do something that I mean the driver to do something. Allow, which is allow read only and read write, which allows an application to share memory with the driver. Subscribe, which is automatically done by the kernel. And the application can subscribe to events from a driver. And we still have a function allocate grant, which we need to implement. This is due to the subscribe system call. And we won't get into details for now. So basically what we need to do is to implement this function and return an okay. So with this, we have simply created a driver for our. It does nothing, but it is a valid driver. The last thing that you need to do is to give it a number because every driver in talk is represented by a number and will say Bob constant driver number. You size will be an any custom drivers usually start at zero a so this will be our custom drivers zero a zero zero one. This number can be anything. But basically talk defines a set of standard drivers which are defined in capsules SRC and driver. And it would be a good idea not to clash with one of them. So that's why we chose a which is the smallest number bigger than zero X nine. So, here we go. Okay, so let's see if this compiles. We'll go back to the kernel and micro bit. And we'll just run make. Here we go we have some errors and okay yeah. We need to import process error. Just here, our micro bit crate automatically includes the driver create so in our cargo file. We have included the driver trade and this is why when we run make in the micro bit. It actually looks at our driver here. Of course it says hey we have some variables that we don't use so let's graphics them with some underscore, and if we have some unused imports but we'll use them in a minute. Okay, now, right in this driver is the first step. The second step is to register our driver. So this will go to source main, and this is the main file for talk. In the search here we have a structure which is called micro bit, which basically defines all the driver types that we will use. And at the end, we will say greenhouse, and we'll say it's drivers drivers greenhouse and greenhouse structure. We have defined this driver here. The second thing is to connect it to the kernel so defining a driver here driver type simply defines a value of field in a structure. We have a trade which is called system called driver lookup, which is implemented by micro bit, which has only one single function where the kernel says hey, if you have this driver with this number run this function with that driver. If you don't have it just run it with none. So as you can see the whole function is a match. And it simply matches if the driver number matches the actual driver number it's run it runs the function so we'll just add ours here. So it's drivers greenhouse. Remember the driver number variable if this fits will simply run itself and greenhouse. Here we go. Next, we have a structure micro bit, and we need to initialize our driver. Now the problem is that these drivers have to have a static lifetime. So if we do something like this, like greenhouse equals drivers. Simply that structure greenhouse greenhouse. This will not work. I mean, we can try and we'll say greenhouse here. But this will give us another. Oh, sorry. This needs to be. I think we have a problem here. Oh yeah needs a reference. Now what happens here. We actually asked for one. I think I mistook here driver. I need to define a static reference in the structure. Yeah, here we go. So, here, um, yeah, we have a reference. Now what happens here is, um, oops, so it's called driver is not implemented as static reference should be is we implemented it here. They will do something wrong. Let's see. No drivers greenhouse. Oh, of course. Now, the problem is that if we construct the greenhouse here, it's lifetime will be bound to the lifetime of the function of the main function. Problem is we need a static one for that. We'll use the static in its macro and we will say hey, make us a structure of this type and use this as its initializer. Now greenhouse will be a static reference towards this. Check it will simply make a small error and will say hey we have greenhouse of type unit associated attributed to static in it. Now we will get a rust error. And this will say hey, um, it will say hey, I expected unit but I found a mutable reference to greenhouse. So this is how we know that actually static in it will give us a reference. This and the reference here and we should have a driver. And it basically compiles rust can be rather slow in compilations. Okay. The next thing that we need to do is to try to access this driver from the user space. Now drivers respond to commands. This driver does not implement any commands so even if we try to do something with it from the user space, it won't be useful. So what we need to do is to implement a command for this will go to the kernel to close this one will close the capsules and go back to the kernel and go to Cisco and we'll just copy command. So what happens here is the following. The function command implements commands that are supplied by the user space each command has a number which can be anything that's related to the driver. It decides what common zero one two three is and has two values as parameters and extra it has a process ID the process ID of the process that has issued the command by default or commands return no support. Because they're not implemented. So what we'll do was a match command number, and we'll say like this, if it's command zero, we will return command return success. If it's anything else will say hey command return failure will say error code. No support. I mean, we don't know what you want from us. Now, talks standard says that command number zero has to return success every time. This is the way that an application can detect if a driver is present or not. It will issue command number zero. And if it returns success, that means that that driver is present in talk. Okay, let's build this one more time. And as you can see, it builds. It should work. Now, going back to our application. We'll go to the drivers folder. Now that we have now that we have a greenhouse driver. Let's create a user space for it. So we'll say we'll have greenhouse. That age. And greenhouse.c. And we'll say the first function is Boolean, which says, like greenhouse, it takes no parameters and it will return greenhouse is present. We'll include a CD so that this pool is available. And this counterpart will say include greenhouse that age and will implement the function. So what we will do will say, okay, we need talks, we'll include talk. This is the talks library. And we'll say command. And we turn. Can't remember this by heart. We'll just go to the talk see. And the talk. And we'll go to talk.h search for command. And it's a Cisco return t. Okay. So we'll go back to our file here and say, okay, is equal to command command receives for arguments, the driver number. So we'll say, um, In. Number. Command number. I'll say command zero and two extra arguments will just use zero. Command number that we defined inside the driver. This is the same. So we need to define command driver number to zero zero zero zero zero one. Okay. And we'll say like this if red dot type equals. Let's go. Success. This will tell us if the green house driver is present. Once again, we issue command number zero to this driver. What this will do, it will send the command to the kernel. The kernel will go to main dot RS. We will go empirically. The main dot RS to Cisco look up. We'll call this function with driver. This function will have a match. And this driver number, if it matches with our driver, this function will call the function f with our driver. Consequently, the kernel will get a reference to the driver and call the command function. We will receive here the command number and we will execute it. Let's add some debug actually not will not have debugging yet. And what we will do in main C now is, okay, we'll just comment this out and we'll say, hey, let's check if we have the greenhouse drivers so we'll include greenhouse that's H. And we'll say like this, if greenhouse is present. In test house driver available. Please make sure you add the backslash and here otherwise you won't see the messages as the C library caches them until a new line is found. The green house driver is not available. Let's say, hey, you have an error. Okay. I think we're good to go. So let's go to the applications. So exit the kernel will go to the applications drivers will run make to build our driver. Actually, we build our driver library and then go back to example app. In the example app make file, we need to comment this external libraries because we will depend on drivers and will run make. We have a brand new file. Now, let's upload it. Install. Minus minus serial just to be sure. I'll put my board into bootloader mode. And this is number four. Once again in bootloader. It runs. And here we go. And this is strange. We added the driver best says it's not available. What we did not do is we did not update the kernel. So if we don't update the kernel from the micro bit, even though we compile the kernel and compile the app. When you cannot upload it, the board is still running the old kernel that does not have the driver kernel micro bit make program. And here we go. Compiles the kernel uploads it to the board. And let's see what happens. Okay, reboot the board and we can see greenhouse driver is available. For debugging, we can do something really cool. And we can follow the system calls. So if we go to doc. Colonel. We have a file which is called config. This allows us to enable to debug options. One is trace system calls. The second one is debug loading processes. And we'll just make trace system calls through. And then once again, we'll make program and upload the recompiled kernel to the device. This will print for every system call. Whatever this will print information about every system call that has been performed. This is very similar to as trace in Linux. Let's see. Here we go. It's flashing. Okay. And if we reset the board. Here we go. So we have initialization complete. We can see every system call that has been performed. And you can see that this is actually our command. We can detect this by this driver number here. So call command. Command number zero with two zeros it yielded success. All the others are due to the print us. So now if we hit list, we can see that this is our application. Okay, so let's make the greenhouse driver do something useful. As we don't have a lot of hardware, what we will do is we'll say like this one is. Heating and command number two will be set. That's that run pump control pump. The water and control heating. So let's say like this, if we get number one, command number one, we'll do the following. We'll say, hey, if R2, the first parameter is equal to one. We'll start the heating else. If R2 is equal to two. We'll stop the heating. Otherwise, we don't know what you want. So we'll say, okay, this returns command return failure. And we'll say error code. I think the best is no super invalid invalid. So it's an invalid command. For start heating, when we actually do because we don't have an actual heater, we'll say, hey, Colonel, we'll import debug. And we'll say, we'll just print the message. We'll say debug, debug, and we'll return command return success. Everything went well. If we want to stop the heating, we'll say, hey, sorry, this is zero not one debug off. There we go. Command return success. And we will do exactly the same thing for the water control, just that all right. Watering on. Okay, and this will be number two. Sorry. This is our new driver. So let's use it from the sea. Let's say like this blue house. Control. Sorry. And we'll say Julian on the second one will be control. With the bullion on or just copy them. Hey, let's put them into the sea. And we'll say, once again, this one will be just void. Actually, it will say if it went well or not. So green house driver number command number one. And actually we'll say here, depending on on off, if on office two will send one, otherwise we'll send zero. And second function for the heating will be exactly the same. Just that the number will be two. Okay. So, first of all, let's compile our application driver and let's compile our driver library. And it works. Let's go to sample app and let's do the following. If the greenhouse driver is present will have one greenhouse. Control. Heating or started will say, hey, the heating needs to stay there for five seconds. Of course, in reality, it will be more and otherwise will say false. And we'll just oscillate this one. Let's build the app. Actually, let's let's check if it actually works. So if this works, then we do following. Otherwise, we'll say, hey, I have an error. Also make a loader install minus my serial, select the serial port and you put the word. Okay. Let's actually put a delay here because this is completely. This is really, really fast. So let's say, okay, if it doesn't work 30 seconds. Let's compile the app once again and install it again. We go. Okay. Let's see if we can actually build it. Yep, flashes. And what happens is the following heating control error. We can look at the command. We said command number one, turn it on. It says no support. Why? It says no support because we completely forgot to flash the new kernel. So we modified the driver, but we never flashed the kernel. And we'll make a program. This will build the kernel flashes to the board. And as soon as the kernel is flashed, the driver recognizes these commands and should just write either on. Here we go. It flashes. You can see it flashes whenever these yellow LEDs flash. Now it should work. We simply reboot the board. It says here on, as you can see, this was the result of command one. And then he they're off again, the result of command. Do the print off we can see. So this is the heater off. And it says success in order to print it here off. I'll just take out the bugs because these clutter the screen. So let's flash the kernel one more time without those debug lines. Now, if this is a greenhouse, usually we need to access some sensors, get some information from the greenhouse, and then decide if we need to turn on the heater or stop the heater. So what we'll do is access some sensors in the user space. I'll wait for a few seconds so that the new driver, new kernel is flashed, compiled and flashed. Here we go. It's flashing. Let's see. It's done. We reboot the board here on as you can see. And in the meantime, we can use the console here off. So we just made our driver work. Okay, so let's see how we can get some sensor values and build the greenhouse for that will go to Liptox C and examples. Here we have an application which is called sensors. If we go inside of it, we can see we have here some kind of sensors that we can read. And one of them is humidity and the other one is temperature. So this is how we read the temperature. And this is how we read the humidity. Of course, there's several ways, but we'll just use it, use the simplest one. First of all, we have to decide if the humidity and the temperature drivers are present. And this is done by driver exists. So it provides us a function which is called driver exists, where if we give it a driver number, it tells us if the driver is present or not. What it actually does, it just sends command zero. So let's say for temperature and humidity. Let's check this one. So we'll say, okay, in main dot C. First of all, we'll check if the greenhouse driver is present. If it's present, we'll say like this. We have temperature, we have humidity, just tell us if they exist for that we need to include their libraries. One is temperature college, and the other one is humidity college. Okay, this should actually work. And let's say, okay, print house, flowing sensors. And we'll say, we'll just say one or zero, it's easier. And x t, sorry, we don't need to space here. Maybe. I'll just say one or zero humidity. Here we go. Let's compile the app. And let's get on with this example. Make. Oops, we have a problem. Oh, we forgot this one. These ones temperature, humidity. Okay, install. We'll just install the app jumps out of food loader. Okay. Here we go. Let's reset the board. And it says, Hey, I have a temperature sensor, but I don't have a humidity sensor. So this is how you basically decide if the platform has a humidity or a temperature sensor. Well, for the microbeats, sadly, we don't have a humidity sensor. So we'll just be able to use temperature. Now, let's display the temperature then. We'll say like this, if the picture. Have it. Let's print it. Let's say, Hey, in deaf. Is this. We'll say, um, temperature read synchronous. We can do this like this. And value. And we'll just read them value. The temperature will give us actually in hundreds of hundreds of centigrade. So we need to divide this to 100. So, um, here we go. We'll compile again the app and upload it once again. There we go. And uploads. Okay. And it says 27 degrees centigrade. If you want to transform it in Fahrenheit. That's really easy. On the other hand, we are in Europe and this is what we are accustomed with. I'm really sorry about this. And this is also in the international system. Okay, so now that we have a temperature sensor, let's make a simple application that will use this and say, Hey, if you have the temperature, let's do it like this. This is our greenhouse. We will say, Hey, um, if we have a temperature sensor, we check the temperature with displayed and say, Hey, if temperature, temperature value is lower than, um, let's just buy it and value and value 100. And then just value here is lower than 28, let's say. And the heater on. So we'll say greenhouse control heater. Else. Um, let's turn the heater off because we need to save energy. And we'll say, Hey, Okay. And let's also blink an LED to say if this is on, we'll say LED H and we'll say lead on zero. And this one will be led off. Okay, and we'll just repeat this. Let's say delay less every five seconds on a normal greenhouse. It will be more but that I think it's fine. Now we'll see if we can actually heat up the micro bit. Let's try it one more time. Let's install the app. And let's see bullet. Okay, here is on, because it's less than 28. I'm not sure I can actually pull it or heat it up. I might not be able to do this. But technically, the heater is on and you can see my LED is on. Okay, I, I heated up. It says here off and you can see there's no LED on. I can pull it off a little bit. Not sure that this actually works. Um, yeah, at some time it will turn the heater off on sorry. Yeah, it just turned up the heater. So, here we go. Okay, the last thing that I want to show you is this LED. We control the LED from the app. But let's try to control some hardware from a driver. So instead of controlling the LED here, let's make driver control an LED. For that, we need inside the greenhouse structure. We need to get a pin. So the talk provides an hill, or let's say an interface to control pins, which is called GPIO and actually the interface is called pin. So trade pin. So what we want to do is the following. We'll say, hey, this is the heater pin. This will control our heater. It's a static reference. Actually, we're not making static. We'll just say, hey, it's an A reference. Any trade, any structure that implements pin. Now, we'll make a new function here. And we'll say, hey, whenever you get a driver, by the way, now we have a lifetime parameter here. Anytime you get a driver, we need something that is A, sorry, pin is a static pin. This returns actually the easiest way to return self, and it just returns greenhouse pin. We need to import the hill, so kernel, hill, sorry, GPIO pin. And we'll just write heater on and we'll say self.pin.on, sorry, set, I think we'll set it. And actually, no, we don't need a pin. I'm sorry, we need an LED. We need an LED. We turn on an LED. Okay. And we'll say self LED on and here we'll say self LED off. Okay, so this is the driver. Let's say if it actually compiles on a micro bit, we should get an error in main, but we should not get an error in the driver and we'll still get an error. Let's add an LED here and this is also an LED. Okay. And we are missing lifetime here, which is like this. Here we go. Okay, of course, in main, we have a problem. This will be, this will have a lifetime and we'll say, hey, it's a static lifetime. Whenever we actually create the greenhouse, we'll say, hey, I need you to create a static one. And instead of pulling greenhouse, I need new and I need an LED. So, yeah, this will be a little bit trickier because LEDs on the micro bit are not simple pins and they are part of an LED matrix. For this one, I will open an example and just copy some code, which I will briefly explain. So let's go to here. Talk. We'll just take it in here. Sorry, drivers, SRC. No, not this one. Sorry. This one, I need another file, which is the main file. And we'll say like this LED matrix. I'm not looking at the right file. Sorry. Once again here, my book. Colonel micro bit and main dot RS LEDs. Yes. So. LEDs. And I need this one. This is it. Okay, so the micro bit uses the lead matrix and every LED on the micro bit is not actually in a pin, but is controlled by two pins and an LED matrix. So if you look at the LED driver, it's actually an LED matrix driver. So what we want to do is to use one single LED from that driver from that matrix for that. We talk provides a way of doing this. I mean, not talk library and talk. And that is in the LED matrix here. Actually, I have an example here. I'm going to use a single lead in the LED matrix. We have this code here, which is exactly for the micro bit. And this is what we were going to do rain dot RS. It's here. And in the greenhouse, we need to define this LED and we'll say, Hey, I need an LED from the LED matrix that I can control. This is a macro, which will allow me to do exactly just that. And I will say, Hey, from the LED matrix, which is called LED, get led zero zero. And this is our LED. And now I can supply this to the new function. Here we go. Let's see not found in greenhouse. Oh, sorry. Okay. That matrix driver. Oh, sorry. I lead zero zero. I over. I have overwritten one of my variables drivers. Here we go. I lead zero zero. Here we go. Okay, let's get rid of these warnings. We never use our three and we never use the process ID. So we'll just get rid of them. And we'll just say, Hey, make. Okay, and then make program, we'll just upload the program on the board. Here we go. Our bold is nice 25 degrees outside. So it needs to turn on the heater. And now it's flashing. Here we go. It says here on we need to reboot it here on and the LED is on here. We can see. On the other hand, we can change the LED super easily. So main RS. And if we don't want led zero one, we want the LED in the middle. This is three three. We'll just use the LED in the middle of the matrix. Oh, sorry. We'll just run it, compile it, flash it flashes it on the board. And if we reboot it, yeah, it seems that we have two LEDs on, not sure why. Oh, it's led 2.2. And let's also run the app. I think we did not upload the app anymore. So actually led 3.3 is not right in the middle. It's three. It's in the lower right corner, but I forgot to upload the new app and two LEDs are running. Let's make this one again and install that so the board into bootloader. And here we go flashing new app. And we should be good to go. And yes, we have only one LED flashing, which is led 3.3. I'm not sure this can be seen here. Yeah. Here we go. So what we did today is the following we built a driver for talk. The driver takes commands. It's a simple driver command number zero just tells us the driver is available. Command number one turns on and off the here and come in number two turns on and off the watering system. Probably the heater will be a pin on like an LED which is switched on and off. And the user space part of the driver. So basically the library for the greenhouse with the two functions. And we used it in an application. The application that we have built won't work on any device that runs stock without any need for recompiling. I mean, it's completely independent from the kernel. You can ship the tap file and somebody can load it on their board. So they don't need to compile it. They don't need the kernel. It's completely separate. This is a really basic top tutorial, and it does lack a lot of explanations like how drivers exactly work, why we have wrote written some things there. But due to the short amount of time that we have. This is, I think, the fastest way that you can get running with talk, we strongly suggest running reading the getting started. In the talk repository and reading some tutorials that you can find in the talk repository. We will also publish a book in the following year with a lot of more examples. So thank you for your attention and we will gladly answer any questions on any means where they can be asked. Thank you.