 Nice to see all of you here. My name is Michał and I'm from Poland. So what I do on a daily basis, well, I'm a software engineer. I work for Hammer Harman, usually on the embedded programming, and in my spare time I do Python for the last nine years, and I sometimes deliver Python training sessions for the Polish company called Sages. It looks like it should be pronounced sages, but it's not. So if you want to contact me, here's the data. It will appear also on the last slide. So let me tell you a story. How the stock actually started. So it started at the hackathon. There was a hackathon in Poland called Hack Year, and we went there with a group of friends and we were joyfully programming different applications. And all of a sudden there was some kind of idea. I don't know who came up with that, but the idea was that every project is actually better when it has a thermal printer. So well, two of us actually followed the direction, and after a week or so, it came. So we have a thermal printer and, well, it was small. It worked pretty well with all the power supplies I had at home. It was communicated over UART, and there was a Python library for that. So what the UART actually is for those who don't really come from the embedded environment. UART is the universal asynchronous receiver transmitter, which is basically everything you connect over the serial. So you have one wire to receive data, one wire to transmit data, power, grant, and that's it. So let's run the thermal printer with Python. So it's as easy as download the library. It's almost pure Python, so you can run it on the Raspberry Pi, and, well, it just worked. It should work. Yeah. So after that, a friend of mine came up with an idea. Let's connect it to something smaller. So he took a Low Pi version 1 board and ported the library to MicroPython. So he put it on GitHub. It worked pretty well. He made a few changes to the original library. He removed Python 2 code because MicroPython is a Python 3-compatible language. He replaced the serial module with the built-in machine module and removed the Python imaging library because it's not supported on MicroPython. Then he wrote his own code for printing bitmaps, and it worked pretty nice. So let's have a look at the hardware he was using. It was Low Pi version 1. It is a development board from PyCom. It's meant to develop lower-one devices, but for the time being, we do not really care about it. We just care that it has a microcontroller called Expressive ESP32. It has a UART controller. It has 512 kilobytes of RAM and 4 megabytes of flash. It has a hardware floating point, but we do not really care about it. And it runs MicroPython. So it comes with the MicroPython installed on it. It went pretty nicely, but I thought that we can try to run the code of this library on something smaller. So I thought about ESP12e, which is a well-known Wi-Fi device, Wi-Fi module. Those who are using NodeMCU and they are probably quite familiar with that. It can be controlled with the AT commands over the serial, but it appears to have a fully functional MCU inside. So let's have a look at the MCU. It's ESP8266, which is a smaller processor than the one on Lowpy. It has a UART controller, so we are fine to get our printer running. And there is a MicroPython firmware available for that. So you can actually have the official part of MicroPython running on it. And the best thing about it is that you need about $8 to buy the development board. So I took the board, I took the code, and I tried to run it. Well, it has a significant drawback. Although it has 4 megabytes of flash as Lowpy, it has only 36 kilobytes of RAM. And when you load the MicroPython interpreter on that, it leaves you with about 25 kilobytes for each user. So the first thing was to upload the library onto the board and import it. It failed. So it all ended up with the out-of-memory error, but it occurs that it's not really the size of the library itself. We need to remember that byte compilation needs RAM. So this is the thing we're not really thinking about when importing modules on a regular PC, but when you have 25 kilobytes of RAM, you need to consider that the import statement needs RAM to run. So what can we do? Well, we can precompile it to the bytecode on a PC and then load it. And fortunately, the cross compiler comes together with the MicroPython source code, so you just download it from the GitHub. Then you compile the cross compiler, and then you compile the MicroPython module to the bytecode, and it should work. And it worked, actually. So I downloaded the cross compiler. It was as easy as one make. Then I ran the cross compiler, I cross-compiled the module, imported it, and it worked. So now we should be fine. We have the MicroPython library running on the MicroPython-powered device. So the first thing is, if you want to print something, we need to interact with UART. So yeah, we need to configure UART. We need to send the data. So let's focus on the configuration. So the machine module, which is the built-in module in MicroPython, has all the hardware-related functions, including the ones to manipulate UART. And the library itself uses this particular class to interact with your serial device. So this is the code that's actually inside. It looks pretty nice. It just creates the UART object. It sets up the baud rate. It sets up the pins. It's connected to what can go wrong. Well, although it worked pretty well on LowPy, it didn't work at all on my small device. Why? There were extra keyword arguments given. It appears that it doesn't support the pin parameter. So it worked well on LowPy. It didn't work on my device because of this pin parameter. So the second thing is that you need to remember that even if there are similar-looking functions or APIs in different MicroPython implementations, that might not really have the same API. So what now? Well, try not to assume that the code will work on your hardware. If it's possible, rather take the hardware and test it. So I just came up with a very simple solution. So I added the pin parameters, which can appear or not. If it appears, it's passed to the constructor. If it doesn't appear, it's not passed to the constructor. It works on both things. So it worked somehow. Yeah, this is probably the problem. It worked. But to print the test page, it took us about 20, 30 seconds on Raspberry Pi. It takes us a minute and a half on my device. So the suspicious code was here. So we have the microtime module, which looks pretty much like the time module in Cpython. And it has the time function, which works pretty much the same as the time function in the Cpython. So what we can see here is the poor man's buffer overflow protection for the printer. So we set up the timeout and we check if we wait long enough not to overflow the buffer. So and this is the function that actually uses the code. So it waits a particular amount of time. And if it's over, it continues printing, nothing too special. But if you read the manual, it occurs that the time function is a bit different than in regular Cpython. It operates on integers, not on floats. And it has one second resolution. And if you want to have a different precision, you should use the micropython-specific functions like take microseconds or take milliseconds. So what now? Well, read the manual and change your code. Sorry for that. Wasn't really expecting. Yeah, black arrow on the black screen. Good. So I'm probably here. So the solution seems pretty easy. Let's have a tick milliseconds and operate on that. However, we shouldn't use the regular plus and minus operators because we have special functions that that should add and subtract the microsecond based timestamps to make sure that they're not wrapping around. So, so I changed the code and it worked very well on my device. It didn't work on Lowpy. So it occurs that there is no microtimetic add in Lowpy's micropython. So well, even the standard or the modules that seem to be standard for micropython may appear that they have different APIs. So what now? Read the manual again, test it on the other hardware, maybe write a wrapper. So I came up with a very simple solution that adds and subtracts the milliseconds if we have the appropriate API and just uses the plus and minus operators if we don't have the API. So later on, it ran. And it runs with a decent speed, however, it runs with a slightly different speed on Lowpy and it's slightly different, different speed on ESP12e. So I was, I just started to look what can actually be causing that and it occurs that maybe just maybe we're waiting too long on one device and maybe not waiting at all on the other device. I started experimenting with the code. I ran the interpreter. So I just came up with two timestamps. One is the old timestamp, the second one is the new timestamp. And I started to look into the thick diff method. So on Lowpy, it looks like that. It works like that, but if you run it on the ESP8266, it's a bit different. So yet again, if you look again at the manual, well, it says that the old value should proceed the new value on Lowpy. And when you have the good old ESP8266, it looks that, well, it should take the arguments as the subtraction operator actually takes time. So what can you do about it? Well, I don't know yet. But you also need to consider the thing that if even every API call is the same and takes supposedly the same parameters, it may use them in different way. So maybe I should come up with writing a wrapper that will detect the behavior. Well, probably I should test it on as many boards as possible. So let me sum up the things. So first thing is that, well, MicroPython is not the party we're accustomed to. MicroPython on different hardware may actually differ. And they do differ. And embedded devices resources are rather constrained. So you need to take care of things like bytecode compilation, bytecode course, cross compilation on your PC. And you should probably never trust your gut feeling and read the documentation. It's documentation instead, because it really says that in the documentation. But you should keep in mind that MicroPython, documentation on micropython.org is the documentation for the, let's say, official version. And you may see the MicroPythons on different devices that are also called MicroPython. And they behave different, and they usually have the documentation on the vendor's side. And it may lead you to different conclusions. Well, probably the good idea is to test your code on as many devices as possible. And I find it a terrific idea to share your code, put it on GitHub, let people test it on as many devices as they have. So one more thing for the very end of my talk is maybe, just maybe, this is a time for something like MicroPython standard and building up the community around MicroPython. So we can say if something is really MicroPython or just a MicroPython-ish language. So that's all for me. Thank you for listening. It's time for the Q&A session one. If you want to approach me well, I will be here today and tomorrow. If you do not feel like talking to me, you can write me an email. Thank you. Thank you very much. Awesome. Thank you, Mikaela. Have we got any questions? Hello. Is there any online resource where you can check whether or not code you're right on one board is going to be compatible with codes on another board? Or is there any way of checking ahead of time? I'm not aware of that, at least at the moment. OK. Thanks for the talk. So this reminds me of me hacking away on microchip pickaxe controllers, on 8-bit microcontrollers back in school. So how have you found this compared to Arduino, which is probably the leading platform for embedded kind of low-power 8-bit, 16-bit, and what's from your slides? I couldn't tell if you were actually typing into an interpreter and it was on the fly passing messages to the device, however, you were doing some sort of flashing or compiling process to basically load it onto the device and then run it. So actually, it's both. So I was writing programs and flashing them onto device, which is amazingly simple with this particular device because all you need is just the Wi-Fi connection. And I was also using the interactive interpreter. You have the web REPL, not the web REPL, but the REPL running. If you connect it through this serial, you have the REPL so you can have your code written and executed just after pressing the enter. So this is probably the main difference between Arduino and this kind of device because the firmware, like the real firmware, you flash over the serial wire. It happens only once when you're loading or upgrading the MicroPython version. And then just the MicroPython code is a regular file that you upload over TCP IP onto the device and then you run it. So and the other thing is that you can interact with your code with the interpreter over the serial or the TCP IP based console. I just have a quick question about the memory issues you mentioned. Did you ever consider soldering on a larger flash memory chip? So you can get 64 meg or 32 meg variants? Well, I didn't, but it's certainly possible. While the ESP12 comes with some kind of a metal shield, so we probably need to disassemble it, unsolder the old memory and solder the next one. I think it is an SPI based memory, so I think it is possible. I haven't tried that. Yes, some ESP8266 boards have like a separate chip for the memory. Yeah, but do you mean flash or RAM? Because the main issue here is RAM. Because we have four megabytes of flash, which is plethora for the code and data. But the main thing is 25 kilobytes of RAM that's left to us.