G518's December Adventure Log

On this page, I will log my journey through December 2023, following the principles of the December Adventure.

Day 01

While I didn't write any code that day (in personal context, as I did write some num 1060 ISO code in professional context), some development was done in the previous days, as I will sum up here.

As a way to learn the Rust Programming Language further, I decided to try out Rust embedded programming on the RGBDuino Jenny v1.2, using avr-hal. The board has two WS2812 LEDs (datasheet) connected to pins 12 and 13, and I wanted to control them. I drew inspiration from the ws2812-nop-samd21 crate, and implemented the SmartLedsWrite trait for the ATMega328P, which is clocked at 16 MHz on the Arduino Uno board, which the RGBDuino is based on. I then received an Addressable RGB Led Strip so I could test the library, and it worked well.

I will publish the code as a crate on crates.io after some polishing.

Day 02

On this day, after trying out solving the Advent Of Code Day 01 puzzle (from which I collected one star out of two at the moment), I started disassembling a PG-L28A-CO2 CO2 sensor I received earlier this year, with the goal of accessing the raw sensor data to achieve accurate calibration and measurement.

I found out it had a serial port on its PCB, so i soldered some pins to it and opened up picocom. The default configuration worked right away, with a 9600 Hz baud rate, 8 databits, no parity and 1 stop bit. During bootup, a file path appears which indicates that the cpu name is "sh55". After some research on the MCU, especially the manufacturer name, Jieli Technology, I finally could land on this page that could identify the chip as an AD15N, for which there is a stock bootloader and demo firmware. It's very likely that the firmware is the one in use as messages in the serial console match the ones in the firmware's source code.

In next days, I hope to be able to read the MCU's ROM.

Day 03

Today was a bit more spread out. I worked on several different things:

Day 04

I couldn't do much today because of my job, but I managed to cram some lines of code in.

On the official website of the ZJ-5811 thermal printer, a programmer's manual is provided, listing control character sequences for the device. While reading it, it made me want to develop software that would allow to write receipts and send them at a fairly low level to the printer. And what better opportunity to learn Rust than this project? As I'm still a Rust newbie, I don't have a firm grasp of the language's design patterns yet, so I struggled to find how to define the printer in Rust code, having too many concurrent objectives (make the code reusable, printer independent, easy to implement, …) and asking myself too many questions. So I decided to steer away from the top-down approach and directly started writing structs that represented data to be sent to the printer instead. These structs would hold the argument data to be translated into commands to the printer. The objective here is to (interactively?) write commands into the software that would be stored in an intermediate form and then translated into commands specific to the printer.

As of now, I need to write the rest of the structs, and then start implementing traits that would convert the structs' data into printer instructions. Maybe then a way to define the printer will emerge.

Day 05

Not a lot today, but I did reorganize some code for an M5StickC-Plus with the objective of lengthening battery life. In short: the program was still refreshing the screen when sleeping, which is useless, as the screen is turned off when sleeping. I similarly turned off other actions that were not needed when the device is asleep. I'll write more in detail about the M5StickC-Plus later, as there is a lot to cover and also because I didn't unlock its full potential yet.

Day 06

On this day, I tested the battery life of the M5StickC-Plus running the new program. It has indeed improved. I also started rotating in my head the idea of making end-of-year celebrations themed animations on the smart led strip. Lastly, I made some more structs for the POS58 printer control program, which gave me time to think more about how I should implement it. I'm pretty sure I'll make a sort of TermalPrinter trait, that has enum types for some options choices (such as FontA/FontB), and control events as functions, that will be then implemented for the specific printer.

Day 07

I am really exhausted lately, so I couldn't do much, but that's ok, December Adventure is chill after all. Still, I made progress on the thermal printer Rust program. I started writing the ThermalPrinter trait, with types and functions taken from the manufacturer's manual. I also started reading the official Epson ESC/POS command reference, which the POS58 partially implements.

Day 08

I had to call in sick today, so I had some spare time. I made a lot of progress on what i call now the pos58ctl program. I restructured trait and type definitions, defined the ThermalPrinterCommand trait, and defined a POS5811 struct that implements the trait. A lot of control functions were implemented for this specific printer too.

Day 09

On this day, I pretty much finished the pos58ctl Rust API. I finished implementing the printer control sequences, which made me fix up the ThermalPrinterCommand trait, and started playing with some test receipts. I really hope to package this as a crate at some point, I need to document and clean up the code first. I also started refactoring the code to make it more modular and extendable.

Day 10

I decided to set the thermal printer aside for the moment and spent the day trying to program the M5StickC-Plus in Rust using esp-idf-svc. I achieved simple things such as logging messages in the serial console and making the LED blink, I then could control a smart LED strip with the ws2812-esp32-rmt-driver crate. But one thing I spent the whole evening on, was learning how to use the display with mipidsi. First issues were about finding out how to program usage of SPI, realizing I needed to install the display-interface-spi crate for mipidsi to work. Then, when the screen wasn't turning on, I also had to realize that I needed to control the AXP192 power management unit. I tried using the axp192 crate but got no luck so far, it could be how I'm setting up I2C, or something else. Either way, when launching an app written for the M5StickC-Plus in the Arduino IDE that sets up the AXP192, and then launching the Rust code, I can see stuff happening on the screen, so at least this part is OK. I may need to extend the axp192 crate for it to work, or I may need to dig further into M5Stack's control C++ code in order to find out how to control the power chip.

Day 11

I could just read some source code for the axp192 crate in order to troubleshoot control issues but that's all.

Day 12

I finaly could understand the AXP192 problem: voltage values are to be written in mV, so what I was writing was too low of a value and effectively set the output voltage at 0 V. Then I started troubleshooting display issues. After setting the correct reset pin, I could fill the screen with a given color, and found out I needed to invert color values, but couldn't do anything more. I was trying to draw a line as in the embedded_grahics example but it wasn't working. With mipidsi I was trying the ST7789 display driver model with various options but the line wouldn't draw. In a last attempt, I changed the model to ST7789_pico1, and it worked! I think I'll try to find correct options for the ST7789 (and from a quick look at the mipidsi source code I think I found them), as the screen is only 135×240 and the framebuffer is 320×240, and I want to use the full framebuffer to have more graphical memory and then use scrolling tricks.

Day 13

Didn't write any line of code today because of a lack of time and energy but I did start setting up a smart LED lightbulb: the LSC Smart Connect G95 Gold. It says "tuya powered" on the box, which I'm hoping, from a quick search, means I can change its firmware, because for the moment, the stock firmware from LSC Smart Connect isn't great, as it requires an account and internet connection to operate, which, for a lightbulb, is pretty absurd. Also, at first, the lamp flashes to indicate it needs to be setup, and that's not brilliant when the LED is rated at 806 lumen, it's very bright and thus, aggressive to the eye, until it's finally setup. Anyway, I also got a power plug from the same brand and I hope I'll be able to make silly little projects with it from my silly little ideas.

Edit: It appears that the lightbulb runs stock Tuya firmware, and that the LSC Smart Connect app is a clone of the Tuya Smart app.

Day 14

I'm on sick leave so I'll have a lot more time and energy to tinker around. I practically spent the whole day mainly working on the IoT lightbulb.

With this forum topic as a starting point, I first searched around on how to change its firmware and gathered some clues towards either Tasmota or ESPHome as replacement firmware. Solutions for flashing the firmware over the air, not needing to open up the device and make a serial connection, appeared to be tuya-convert and tuya-cloudcutter. Seeing that tuya-cloudcutter had some very recent contributions, on top of being recommended by the ESPHome device page for a very similar lightbulb of the same brand, and explicitly listing the model I had as supported, I went with it.

I had to fiddle around a bit, by modifying the device for tuya-cloudcutter to recognize a newer firmware the lightbulb was running (I should send a pull request) and I was lucky enough for it to work and not brick the lamp. I chose to flash the ESPHome-Kickstart-v23.08.29_bk7231n_app.ota.ug.bin provided custom firmware, which allows to test pins and their functions. It allowed me to find out that the output pins were the same as the lamp on the ESPHome Devices page, so I used its basic configuration and the configuration created by the esphome wizard as inspiration.

So I looked around the ESPHome documentation and it quite frankly blew my mind. There is so much functionality and configuration available just by writing a few lines of YAML. It's very powerful. So I built a firmware image for the BK7231n microcontroller, flashed it using ESPHome Kickstart's OTA, made some tests and improvements to the LED's configuration, and finally got it to run a small webserver for a REST API. So now I can control the lamp using HTTP requests via curl, for example, or HTTP Request Shortcuts on mobile.

I got very excited in very little time about home automation, and I feel that it's only the beginning. Planned next is flashing ESPHome on the power plug, and adding support for better suited protocols such as MQTT.

Day 15

I struggled for quite some time with the flashing of the power plug, but I finally stumbled onto this forum post that describes the exact instructions I needed. Indeed, the plug is mislabeled, and I was trying to flash the wrong chip. I'm lucky enough that the device wasn't bricked. I used information from the forum post and the basic configuration from the ESPHome device page to create a YAML config for the plug. Now I can control both the lamp and the plug using the HTTP Request Shortcuts Android app, even using multi-shortcuts to control both from a single button, it works very well.

Additionally, I made some progress on the M5StickC-Plus controlling decorative lights, by tuning some animation effects and making it draw a tiny pinecone on its screen.

Day 16

Most of this day was spent creating a WiFi access point and an http server on the M5StickC-Plus' ESP32. The esp-idv-svc's documentation was of limited help but its examples were very useful. Now you can access a form hosted on the ESP32 that allows you to set the light's color and brightness. It also works by just sending a POST request to the device too. In addition, the main button now can be pressed to change the light's animation and turning them on and off.

Up next, I'm hoping to create a language for an intermediate representation of the lights' animation. It would be a sort of virtual machine that controls a 1-dimensional turtle. I'm very excited about it and can't wait to get started. I also want to implement some 1-bit music functionality, as the device has a little buzzer. But before all of this, some big refactoring is long overdue.

Day 17

This day was refactoring day, it was and still is much needed. My objective is to make the code cleaner, easier to understand, and more modular, which would make it more portable.

I'm struggling hard. Surprisingly, converting the code to concurrent threads was the easiest part. There is now a thread that reads button state, a thread controlling the led strip, and the server endpoint handlers that were already there. Where I'm hitting walls is rather stuff about ownership, borrow checking, type syntax and trait bounds syntax. I need to get more familiar with those and that's precisely the occasion. For me, the part I struggle the most with is extracting code fragments to functions, it causes a lot of type problems and needs additional error handling.

Day 18

Still spent most of this day refactoring. But this time it was more successful. I finally started wrapping my head around Rust's type and trait bounds syntax. With this, I could extract bits of code in main() as standalone functions, and then I could move those functions to separate modules. The code now looks way more organized, and allows to focus on particular aspects without worrying about the others.

I still need to document the code though, and change error handling for the program to panic only when strictly necessary (it was acceptable for a prototype but this is getting serious now). And I'm still eager to start working on 1-bit music and what I decided to call 1DST: the 1-dimensional stack turtle.

Day 19

Today was mainly spent writing a specification for the 1-dimensional stack turtle, and trying it out with a prototype written with the SDL2 library. I got a kind of version 1 working, after many adjustments to both the code and spec. The document will be available on this website soon. I intend to make people read it and send 1DST programs to the LED strip. At first, the prototype was built strictly around an interpreter, but it quickly became apparent that a fully fledged parser was needed, along with a sort of assembler, for the control language to really shine through.

Then I started writing another prototype but for the audio part, with the SDL2 yet again, used to output 1-bit music as square waves.

Day 20

Today was a successful day. I managed to finish implementing the 1DST on the M5StickC-Plus and make the buzzer work.

For the 1DST, a lot of modifications to the prototype were needed, for mainly one reason: telling the turtle to stop. Indeed, we want to be able to stop the turtle when we want it to load another program, different from the one already running. It took me quite some time and trials to get it right, but I believe it is done. It works with channel messages for starting a new program and sending a termination notice, there needs to be checks for termination in different places due to how the turtle was (badly) implemented. And it took quite some tuning for the turtle to behave as expected in regards to starting, stopping, and resuming. I intend to publish the source code one day, for what I call the world's most overengineered christmas lights. But now from the devices' html form it is possible to send some 1DST code that gets compiled, run and displayed on the led strip.

For sound, it was a struggle to troubleshoot, there was simply no output from the buzzer, no matter how it was controlled. After some time and a lot of research, I started to get a hunch, and that was it: the buzzer needs to be powered from the AXP192. In fact, it is connected to the EXTEN pin of the chip, for which control from the axp192 crate is not yet available. It is from v0.1.2 of the crate but only the v0.1.1 is on crates.io, which is odd. Anyway, it was an opportunity to learn patching with Cargo. Then it was just a matter of telling the chip to turn EXTEN on. As in M5Stack's Arduino implementation of the M5StickC-Plus' control interface, I wanted to control the buzzer with ledc, but I also wanted to be able to change the pwm frequency on the fly, in order to play different music notes. Well it turns out it isn't possible with the esp-idf-hal Rust crate yet. But it is possible with the C interface. Thankfully, I found this github issue which allowed me to patch the crate too, and it worked flawlessly.

Now, all that's left for this project is to implement 1-bit music, clean up and document the code, and then hopefully publish it. The 1DST spec also needs to be finished and published in order for people to send me their programs to try out.

Edit: The specification is out: 1DST Specification

Day 21

On this day I finished and published the first version of the 1DST specification. Then I polished the PC simulator and published it on SourceHut. I could then focus on porting the music system prototype to the M5StickC-Plus, after having written a short, simplified 1-bit version of Jingle Bells. I first copied the PC prototype to a version that runs on its own on the M5StickC-Plus, without all the other bits of code, so that there is less to write in program memory (it shortens write time and allows for quicker tests), and to check if it works by itself. Then, I could copy it over to the main program, as it was already in a module it could be done quickly, I just had to alter it slightly so that the music could be stopped and started from pressing the B button.

At this point, I consider the "end-of-year" project, the most overengineered decorative lights, finished. Just in time for when I need it, as I intend to show it to my family for the holidays.

Day 22

Today was a light day, I used the pos58ctl program and the thermal printer to write name stickers for presents. Then I took the train to my grandma's house for the end of year holidays. I could show her the LED lights project, she seemed to really like it, and came up with interesting 1DST program ideas, such as making a program out of other ones that cycles between different animations. I had not thought of that until now. So I spent some time writing 1DST programs to try out and, for convenience, saved them in the HTTP Shortcuts apps, so they could be sent quickly to the turtle from a press of a button.

Day 23

Did not do much programmation-wise today, I was fixated on fixing my grandma's car's fuel tank filler cap (at which I'm not successful yet). But I did discover the ESP32 Marauder firmware, which is pretty interesting, and supports the M5StickC-Plus, pretty excited to try it out. So I fought a bit with the arduino IDE in order to compile it and finally succeeded, just to find out later that there are compiled binaries in the releases and github actions pages.

Conclusion

At first, I wanted to continue the december adventure for the full month of december, but I realized I was really tired and did not have the enregy nor the inspiration anymore.

Still, this month really was interesting, and motivated me to log my daily activity more, so I will probably do that on this website in 2024. As of now, I'll probably try to learn toki pona and enjoy what's left of the holidays, happy new year.