Mousefood – Build embedded terminal UIs for microcontrollers
Posted by orhunp_ 6 hours ago
Comments
Comment by zokier 4 hours ago
You have a bitmap display, you can just draw lines and stuff without needing to rely on font-based hacks.
Comment by weinzierl 4 hours ago
Text based graphics with fancy or custom fonts is just crazy efficient. That is exactly how we got the amazing graphics of The Last Ninja or Turrican on machines with less than 64KiB useable RAM.
Same for more modern embedded devices. If you constrain yourself to text you increase both runtime performance and your developer productivity.
Comment by orbital-decay 3 hours ago
Comment by weinzierl 3 hours ago
[1] Except for the early oscilloscope style vector displays maybe.
Comment by gmueckl 3 hours ago
Comment by weinzierl 3 hours ago
EDIT: If you mean they were not copied in a frame buffer first, you are right. I should not have written 'blitting'.
Comment by awkwardleon 2 hours ago
Comment by adiabatichottub 2 hours ago
Comment by direwolf20 2 hours ago
Comment by LoganDark 2 hours ago
I believe on some systems there were some tricks that allowed some bitmap display by redefining glyphs. One example off the top of my head is The 8-Bit Guy's Planet X2, which can use text mode but with glyphs redefined to use for icons, units, terrain, UI, etc.
Comment by orbital-decay 3 hours ago
Comment by weinzierl 2 hours ago
My original point that putting a fixed number of small and fixed rectangles on a screen is more efficient than line drawing still stands though.
Comment by codebje 1 hour ago
Without dedicated sprite hardware it's not more efficient to read a byte from one place and write a byte to another than to write background bytes and write line colour bytes. DMA controllers on µCs won't save you: a character is usually something like 8x8 or 8x16 and displays are rarely more than 8 bit, so we're talking about DMA transfers of just 8 bytes each, and the overhead of setting them up more than offsets any efficiencies gained.
An 8x12 cell, for example, is 96 pixels to be transferred in 12 rows of 8 pixels. That's 96 reads, 96 writes, and (assuming an unrolled inner loop) 12 branches, to copy as a sprite. Or, it's 96 writes and 12 branches to clear, and (horizontal line) another 8 writes to draw, no branches.
When your graphics become too complex for simple drawing routines to handle them, they're probably also too complex for simple character ROMs, too.
Comment by Johanx64 23 minutes ago
There's quite a few ways to do this, you can do a DMA transfer per horizontal/vertical screen line (not enough memory for a fullscreen buffer, but usually enough memory for 2 fullscreen lines), with an interrupt which fills in the next line to be transfered, etc.
> displays are rarely more than 8 bit
Backing memory in these color TFT SPI displays is often 18bits per pixel, often transfered as RGB565 (2bytes) per pixel.
For SSD1306 its 1bit per pixel, and even the weakest MCUs usually have enough memory for a second buffer.
All this is completely ass-backwards thinking though. The crucial question is - does the end-user/customer want to see smooth lines or prefers "hacker-man" TUI aesthetics.
I'd say you generally speaking users want normal smooth lines graph instead of hackerman aesthetics.
So preferring implementation simplicity (TUI) might be another case where substandard programmers prioritize their convenience over the end-user needs/preferences.
Comment by zokier 3 hours ago
Comment by LoganDark 2 hours ago
Comment by Johanx64 1 hour ago
So in order to draw a line, you will - objectively - have to copy/move more bytes if you approximate line with character symbols.
This isn't a big deal, but crazy efficient it is not.
All the efficiency when drawing on those screens mostly relies on how well you chain together DMA transfers to portions of the screen you want stuff to be drawn to, so that SPI transfers aren't blocking the CPU (that's assuming you don't have memory for a second full-screen buffer).
Comment by duskwuff 20 minutes ago
ST7735 is more of a standard (color) bitmap display.
Comment by Johanx64 9 minutes ago
It's very easy to use it as a generic bitmap display, there's nothing awkward about packing 8pixels into 1 byte, and you can set the addressing mode (horizontal/vertial) to whatever you want, etc.
Comment by nine_k 4 hours ago
Hence 100% Rust. Works on ESP32, RPi2040, and even STM32. Several displays mentioned, including e-ink.
Comment by Liftyee 2 hours ago
Comment by VorpalWay 1 hour ago
Rust on embedded uses a HAL layer, which is vendor independent (and resolved at compile time, like templates would be in C++). It doesnt cover everything yet, but basics like GPIO, SPI, I2C etc are covered. This avoids the issue of N drivers times M vendor SDKs: I2C drivers can just be written against the HAL, and you instantiate with a specific HAL in your application. Also reduces vendor lock-in. The setup process still requires some chip specific code to select which pins to use etc, but once you are past that you can be vendor neutral.
Speaking of which, the API uses some clever patterns (called typestate) to ensure at compile time that your peripheral config is valid: if you "take" GPIO2 you can do that again, so you can't give the same pin to two different pieces of code by mistake. And if the driver expects a pin configured as output you can't give an input pin (you can convert a pin to "dynamic at runtime" if you really need to, so there is an escape hatch).
Then there is the embassy framework. This is an alternative to RTOSes (there are some Rust RTOSes as well, haven't tried them). It makes use of async/await tasks in Rust that are statically allocated and scheduled. You can have several priority levels of schedulers (even though internally the schedulers are cooperative for the tasks inside, but they are preempting between schedulers by using interrupts).
Async actually makes many things on embedded easier, such as waiting for a GPIO. No longer do you need to write your own interrupt handler, or figure out when to put the chip on a low power state, the scheduler and HAL futures do it for you.
All that said: C++ still is a larger ecosystem with more tutorials, drivers and better chip support. But that is advancing rapidly in the Rust world. ESP32 series has official vendor support in Rust for example, as does at least one or two other vendors (or they are in the process of adding it). Popular chips like the RP2040 etc have support, and I have seen HALs for NRF and ST around (but never played with them). Drivers for common chips exist.
So I would say it is worth experimenting with at least, but you should check up front what HALs and drivers exist for what you want to use and check how complete those are. Two years ago I wanted to do I2S things on the ESP32, but that was still missing. A year ago it had support, but some of the DMA things were clunky still. I should check again some time.
Comment by orhunp_ 4 hours ago
I'm currently live on YouTube (doing some maintenance & testing). Feel free to join if you have any questions!
Comment by piskov 1 hour ago
(thank god it isn’t; why do people drag web everywhere is beyond me)
Comment by onjectic 4 hours ago
Comment by LoganDark 2 hours ago
I first learned about password hashing when I tried to make the actually most secure door lock program. I first used raw SHA-256, but then someone on the forum introduced me to PBKDF2...
Sometimes I miss those days.
Comment by orhunp_ 4 hours ago
Comment by wjholden 4 hours ago
Comment by GeertJohan 4 hours ago
Comment by orhunp_ 4 hours ago
Comment by dbacar 5 hours ago
Comment by orhunp_ 4 hours ago
Comment by nine_k 4 hours ago
Comment by 01HNNWZ0MV43FF 4 hours ago
Comment by 0xbadcafebee 2 hours ago
Comment by IamDaedalus 4 hours ago
Comment by redanddead 4 hours ago
Comment by GeertJohan 4 hours ago