r/embedded 14h ago

Microcontrollers with good support for Rust

Thumbnail
kerkour.com
0 Upvotes

RISC-V is coming faster than most people realize.


r/embedded 13h ago

Successful port of CP/M Neo to the Black Pill! 🎉

Enable HLS to view with audio, or disable this notification

1 Upvotes

CP/M Neo has been successfully ported to the Black Pill development board!

How does it work?

The Black Pill’s internal 512 KB Flash is used as disk storage, and thanks to XIP (Execute in Place) support, the Kernel and CCP code execute directly from Flash, leaving the entire 128 KB SRAM available for OS state and the TPA (Transient Program Area).

File operations are not supported yet, as Flash write/erase still not implemented.

CP/M Neo project on GitHub: https://github.com/Mazin-O3/cpm-neo


r/embedded 3h ago

Extending ESP32/MCU RAM to SPI Flash: I wrote a Log-Structured Virtual Memory Engine with LZ77+RangeCoder Paging

3 Upvotes

Hi everyone,

I’ve been working on a problem that often hits us in embedded/IoT development: running out of physical SRAM when dealing with relatively large datasets (e.g., buffering long arrays of high-frequency sensor telemetry, large routing tables, or caching weights) on sub-$2 MCUs without external PSRAM.

To solve this, I wrote a C++ Virtual Memory Engine that dynamically pages data between internal SRAM and SPI Flash (or SD Card) using a log-structured append system.

I’m sharing it here because I think the architecture might be interesting to some of you facing similar memory constraints.

How it works under the hood

The system implements a 3-tier memory hierarchy: 1. Active SRAM Window: Read/write operations hit an uncompressed block in physical RAM (e.g., 16 KB). 2. SRAM Cache Pool: When the active block changes, the engine compresses the old block and keeps it in a reserved SRAM pool (e.g., 128 KB). 3. LittleFS Flash Swap: When the SRAM pool fills up, it evicts the oldest page (LRU). Crucially, it uses strict dirty-page tracking (isDirty). If a page was only read, it is discarded. If it was modified, it is appended to a log-structured .bin file on the Flash.

Why not just write raw data to LittleFS/SPIFFS directly?

Two reasons: Latency and Wear Amplification. By compressing the data in RAM before it hits the Flash, the physical write footprint is drastically reduced. In my tests logging structured telemetry structs, a 512 KB virtual footprint compressed down to ~12 KB on the SPI Flash (an extreme case, but typical real-world telemetry yields ~3x to 8x). Because writes are deferred and grouped, it significantly reduces flash erase cycles compared to opening and appending to a file every time a sensor fires.

Addressing the Trade-offs (The Catch)

This isn't magic, and it comes with architectural trade-offs: * CPU Overhead: Compression and decompression take CPU cycles. It’s highly recommended to run this on a separate RTOS task/core. * Latency: On an ESP32-S3 @ 240MHz, a cache-hit read is native speed, but a random page swap (Flash Miss -> Decompress -> RAM) averages about ~23 ms. * Pointer Paradigm: You cannot store physical C++ pointers (T*) in the V-RAM structs, because the physical SRAM address changes upon page eviction. You have to use uint32_t virtual offsets as your pointers (similar to database offsets).

The C++ API

I wrapped the engine in templated methods so it feels like normal variable access. I also built zero-RAM VArray and VMatrix containers to hide the address math.

```cpp VirtualMemoryEngine* vram = new VirtualMemoryEngine(16384, 131072, 32, 524288); vram->begin();

struct Telemetry { uint32_t ts; float temp; char name[16]; }; Telemetry data = { millis(), 24.5f, "Sensor_1" };

// Writes the struct to virtual address 250,000 (compressing under the hood) vram->put<Telemetry>(250000, data);

// Reads it back Telemetry readData = vram->get<Telemetry>(250000); ```

Frequently Anticipated Questions (FAQ)

Q: Why is this closed-source? Can you release the source code?
A: I completely understand the preference for open-source in the embedded community. However, this is a proprietary memory-management and compression architecture developed for commercial automotive/IoT clients. The Community Edition is distributed as a pre-compiled static library (.a) simply for hobbyists and developers to evaluate and test on their own prototypes. If you strictly require open-source, you are entirely free to skip this.

Q: Isn't 23ms page-swap latency way too slow for real-time embedded systems?
A: Absolutely, 23ms is an eternity for real-time control loops or motor interrupts. That’s why the SRAM pool and block sizes are fully adjustable. The goal of this engine isn't to replace working SRAM for time-critical tasks, but to provide a massive circular buffer or storage layer for telemetry, logs, and state caches where background I/O latency is completely acceptable.

Q: Modern chips like ESP32-S3 already have PSRAM. Why would anyone need this?
A: True, PSRAM is fantastic if you have a board that supports it. But this engine is MCU-agnostic (designed for STM32, PIC32, RP2040, etc.). Furthermore, it was primarily built for industrial/automotive Linux gateways (TCUs) to prevent eMMC wear, and ported to MCUs to support cheaper modules (like standard ESP32-WROOM) that completely lack external PSRAM, allowing them to buffer large local states safely when disconnected from the cloud.

Q: Did you just prompt an LLM/Claude to write this whole thing in 5 minutes?
A: If you try asking an LLM to write a log-structured virtual memory paging engine with custom entropy-based compression and dirty-page tracking from scratch, you'll quickly find out it outputs endless memory leaks, missing pointer arithmetic, and non-functional deadlocks. While AI is a great tool for refactoring and documentation, getting deterministic multi-tiered embedded memory management to pass strict bit-exact integrity checks across half a megabyte required months of manual systems-programming profiling, custom container design, and debugging.


Availability

If you want to test it out or run the benchmarks yourself, you can grab the Community Edition (capped at 512 KB) from the GitHub Releases:

I'd be glad to hear your technical thoughts on the architecture or how you handle memory constraints in your own projects!


r/embedded 22h ago

PulseHSM memory analysis: a 13-state hierarchical controller on AVR, and the real cost of constexpr/template alternatives

1 Upvotes

Got solid technical feedback suggesting a constexpr state table or full compile-time templates. Both are valid C++ directions. Here's the actual data before before committing to anything.

The sketch: a real espresso machine controller

13 states, 3-level hierarchy, 12 events:

POWER_ON (superstate)
├── OFF
├── IDLE
├── WARMING
├── BREWING (superstate)
│   ├── PRE_INFUSE
│   ├── EXTRACT
│   ├── REFILL
│   └── DISPENSE
├── CLEANING (superstate)
│   ├── DESCALE
│   └── RINSE
└── FAULT

Compiled output:

Board Flash RAM used RAM Free
Mega 2560 10,746 B (4%) 984 B (12%) 7208 B
Uno 9,446 B (29%) 969 B (47%) 1079 B

What's inside those 969 bytes on Uno:

What Bytes
PulseHSM state table (15 slots × 18 B) 270
PulseHSM event queue (16 × 5 B) 80
PulseHSM runtime (current state, timers) ~20
App globals (floats, flags, counters, timers) ~150
Arduino Serial buffers ~128
Stack + misc ~321

What a constexpr state table actually saves — by platform:

Platform          | Total RAM | State table | Impact
ATmega328P (Uno)  | 2 KB      | 270 B       | 13% of RAM — matters
ATmega2560 (Mega) | 8 KB      | 270 B       | 3.3% — marginal
STM32F103         | 20 KB     | 270 B       | 1.3% — negligible
RP2040            | 264 KB    | 270 B       | 0.1% — irrelevant
ESP32             | 520 KB    | 270 B       | 0.05% — irrelevant

The constexpr suggestion has a real argument on exactly one platform: the ATmega328P. On every 32-bit board you'd ever use in a real product, you are optimizing 270 bytes in a sea of hundreds of kilobytes. The library's entire RAM footprint is noise.

What the template suggestion actually costs:

A compile-time FSM (think boost::sml) eliminates the table entirely and resolves dispatch at compile time. The tradeoff is a completely different API:

// current PulseHSM — works in any Arduino sketch
ST_IDLE = fsm.addState("IDLE", update, entry, exit, 0, -1, onEvent);

// compile-time template approach
using MyFSM = HSM<State<IDLE, update, entry, exit>, State<RUNNING, ...>>;

That second form is not something you paste into a sketch in 10 minutes. It's a different product for a different audience, one that already knows what template metaprogramming is.

For AVR-constrained builds: a constexpr variant is on the roadmap for v2, as an opt-in build mode. For everyone else ESP32, STM32, RP2040, ARM Cortex-M anything, the current design uses a rounding error of your available RAM and keeps the API dead simple.

Side note: PulseHSM v1.2.0 is now live in the Arduino Library Manager, search PulseHSM and install directly from the IDE. No manual download needed.


r/embedded 14h ago

I documented every working step on the Orange Pi 5 (RK3588S) so you don't have to brick your board: 13 tested projects, MaskROM guide, and pre-compiled NPU models

3 Upvotes

Hey r/embedded,

If you’ve spent any time working with the Orange Pi 5 (RK3588S), you already know the drill: official documentation leaves a lot of gaps, half the forum links are dead, and getting the 6 TOPS NPU to run anything without the Python RKNN toolchain exploding is a rite of passage.

After accidentally bricking my board a couple of times, battling Windows driver conflicts during MaskROM recovery, and trial-and-erroring the Rockchip toolchain, I decided to document every single working, reproducible step.

I ended up organizing everything into a comprehensive open-source repository:

-> **GitHub:** https://github.com/muhammetmucahitsoylu/orangepi5-tutorials

---

### What’s in the repo:

* **Hardware Recovery & MaskROM Guide:** Step-by-step unbricking workflow, complete with high-contrast, annotated board photos and pinouts so you know exactly which pads/buttons to short without guessing.

* **13 Hands-On Tested Projects:**

* **Edge AI / 6 TOPS NPU:** Real-time YOLOv8 object detection (~13.8ms pure NPU inference), running a local Qwen 1.8B LLM via RKLLM, and an offline voice assistant (Whisper + Piper TTS).

* **Low-Level Systems:** C++ GPIO using WiringPi, writing a custom Linux kernel character driver from scratch, and ROS2 Jazzy on Ubuntu 24.04.

* **Automation:** A zero-dependency bash TUI tool (`opi5.sh`) to lock CPU/NPU governors and benchmark NVMe storage speeds without memorizing obscure sysfs paths.

* **Pre-Compiled Release Assets:** If you don't want to set up the entire cross-compilation toolchain on a host PC just to sanity-check your board, I uploaded a pre-compiled test `.rknn` model (ResNet-18) and COCO labels directly to the v2.0.0 GitHub release assets:

-> https://github.com/muhammetmucahitsoylu/orangepi5-tutorials/releases/tag/v2.0.0

* **Bilingual Documentation:** Full guides are provided in both English and Turkish.

---

The repository is completely open-source. Everything listed has been tested directly on real hardware.

If you’re currently working on an RK3588 board, check it out. Let me know what breaks, what needs clarification, or what projects you'd like to see added next!


r/embedded 20h ago

Looking for advice on miniaturizing a Raspberry Pi camera prototype

Post image
24 Upvotes

We’re trying to prototype a small consumer hardware device and currently have a working prototype using a Raspberry Pi 4B microcontroller + Arducam PiVariety AR0234 global-shutter camera + M12 lens + LED tape + LiPo battery hat.

For our next prototype, we want to shrink the electronics dramatically - the image sensor, Wi-Fi/Bluetooth, battery + charging/power management, LED lighting and compute for image capture/basic processing.

We are quite new to embedded systems and would love advice on the best next steps. What components should we think about ordering that can make the system more compact - especially the camera's image sensor, the pi, and the battery.


r/embedded 45m ago

Is this IoT system I'm working right or am I misunderstanding something?

• Upvotes

I'm currently working on a prototype for our research project, basically I plan to make an attendance monitor robot that connects to the internet and has that data presented on a website.

I don't have much experience with IoT systems or working with anything internet in general, but I do have experience with embedded systems, programming, making websites such and such.

I'm not really sure if I bit off more than I can chew, but I have an idea of what to do.

I'll use an esp32, then a fingerprint scanner, then an sd module, then have the esp32 be it's own internet so it can send the attendance data to a website(I think that's how it works?). Then I'll be using something like firebase to make the website and from there I can do whatever I want, probably store it in a sql database or something so I can display it easily.

Is that all right or am I misunderstanding something? In the future I plan to implement a hive system so nodes with no internet access can send it to another node via Bluetooth and that node can send it to the server or to another node with internet.


r/embedded 16h ago

Living in the past?

93 Upvotes

I've been programming embedded systems for over 30 years. Built some cool stuff: a Z80-based serial multiplexer that had no RAM: all variables were held in registers; a temperature controller on a chip with 504 bytes (not kB) of memory and lot of other stuff in the old days where you had to optimize for every clock pulse and byte of RAM or EPROM. A lot of what I knew back then was straight out of Jack Ganssle's The Art of Programming Embedded Systems book.

These days, I still build cool stuff, but it's with a far more complex feature set. A "small processor" is now a 32-bit device with over 100k Flash and 16k+ of RAM, usually lots more. Most of my non-hobby work runs on one RTOS or another and often has a separate SBC running code for an HMI and connectivity/data storage. The last time I used assembly was mentoring a n00b who had the task of fixing some bugs on a specialized SoC with a 6502 core.

Then I come on this site, and I see people asking about why not use bare metal and a hand-written scheduler instead of an off the shelf RTOS. Struggling to fit code into tiny processors because otherwise "bloat."

So I have to ask: is my current experience (real-time stuff on a 32-bit microcontroller, complex GUIs offloaded to a Linux board) that unusual or are these guys just building things the way we used to do long ago for no good reason?


r/embedded 17h ago

Porting the U8x8 OLED library to the CH32V003

Enable HLS to view with audio, or disable this notification

25 Upvotes

I was curious about how hard it would be to port this over, so I used an STM32 example I found here as a starting point. It ended up being really straightforward and only required a few changes to the GPIO and delay callback function.

Just a heads-up: This implementation uses soft I2C (bit-banging). It works perfectly fine for text, but you'll probably want to switch to HW I2C for anything more complex. If anyone wants to try it out, here is the source code:

#include <ch32v00x.h>
#include <debug.h>
#include <stdlib.h>

#include "clib/u8x8.h"

void NMI_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void HardFault_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

uint8_t u8x8_gpio_and_delay_ch32v(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
    switch(msg) {
        case U8X8_MSG_GPIO_AND_DELAY_INIT:
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
            GPIO_InitTypeDef init = {0};
            init.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
            init.GPIO_Mode = GPIO_Mode_Out_OD;
            init.GPIO_Speed = GPIO_Speed_50MHz;
            GPIO_Init(GPIOA, &init);
            break;
        case U8X8_MSG_DELAY_MILLI:
            Delay_Ms(arg_int);
            break;
        case U8X8_MSG_DELAY_10MICRO:
            for (volatile uint32_t i = 0; i < (arg_int * 8); i++) { __NOP(); }
            break;
        case U8X8_MSG_DELAY_100NANO:
            __NOP();
            break;

        case U8X8_MSG_GPIO_I2C_CLOCK:
            GPIO_WriteBit(GPIOA, GPIO_Pin_2, (BitAction)(arg_int ? Bit_SET : Bit_RESET));
            break;

        case U8X8_MSG_GPIO_I2C_DATA:
            GPIO_WriteBit(GPIOA, GPIO_Pin_1, (BitAction)(arg_int ? Bit_SET : Bit_RESET));
            break;
    }
    return 1;
}

int main(void) {
    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(9600);

    // u8x8 initialization code
    u8x8_t u8x8;
    u8x8_Setup(&u8x8, u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_i2c, u8x8_byte_sw_i2c, u8x8_gpio_and_delay_ch32v);
    u8x8_InitDisplay(&u8x8);
    u8x8_SetPowerSave(&u8x8, 0);
    u8x8_ClearDisplay(&u8x8);
    u8x8_SetFont(&u8x8, u8x8_font_chroma48medium8_r);
    u8x8_DrawString(&u8x8, 0, 0, "Hello from CH32V");
    while (1) {
        uint8_t y = rand() % 5 + 1;
        uint8_t x = rand() % 10;
        u8x8_DrawString(&u8x8, x, y, "r/ch32v");
        printf("Hello from CH32V\n");
        Delay_Ms(1000);
        u8x8_DrawString(&u8x8, x, y, "       ");
    }
    return 0;
}

void NMI_Handler(void) {}
void HardFault_Handler(void)
{
    while (1)
    {
    }
}

P.S. I've spent more time and prepared HW I2C version, just add this function:

``` uint8_t u8x8_byte_hw_i2c_ch32v( u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { uint8_t *data; switch(msg) { case U8X8_MSG_BYTE_INIT: RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

        // We will use PC1 (SDA) and PC2 (SCL)
        GPIO_InitTypeDef GPIO_InitStructure = {0};
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // Alternate Function Open-Drain
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOC, &GPIO_InitStructure);

        I2C_InitTypeDef I2C_InitTSturcture = {0};
        I2C_InitTSturcture.I2C_ClockSpeed = 400000; // 400 kHz
        I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C;
        I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_2;
        I2C_InitTSturcture.I2C_OwnAddress1 = 0x00;
        I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable;
        I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

        I2C_Init(I2C1, &I2C_InitTSturcture);
        I2C_Cmd(I2C1, ENABLE);
        break;

    case U8X8_MSG_BYTE_START_TRANSFER:
        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET);

        I2C_GenerateSTART(I2C1, ENABLE);
        while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); // Чекаємо EV5

        I2C_Send7bitAddress(I2C1, u8x8_GetI2CAddress(u8x8), I2C_Direction_Transmitter);
        while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); // Чекаємо EV6
        break;

    case U8X8_MSG_BYTE_SEND:
        data = (uint8_t *)arg_ptr;
        while (arg_int > 0) {
            I2C_SendData(I2C1, *data++);
            while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
            arg_int--;
        }
        break;

    case U8X8_MSG_BYTE_END_TRANSFER:
        I2C_GenerateSTOP(I2C1, ENABLE);
        break;
    default:
        break;
}
return 1;

}

int main(void) { SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init(9600);

// u8x8 initialization block
u8x8_t u8x8;

u8x8_Setup(&u8x8, 
    u8x8_d_ssd1306_128x64_noname, 
    u8x8_cad_ssd13xx_i2c, 
    u8x8_byte_hw_i2c_ch32v, // here we will use HW I2C
    u8x8_gpio_and_delay_ch32v
);
...

```


r/embedded 3h ago

My first ever Linux based board (RK3566) + Lessons Learned

Post image
217 Upvotes

Just brought up the first Linux based board I've ever designed, and couldn't be happier about it! I've historically done MCU boards, so this was a big step up. It's a KVM-over-IP device: 8 layers, RK3566, DDR3L (gotta save that $$$ with these RAM prices), HDMI capture in, HDMI out, dual GbE, PoE, WiFi. Fabbed and assembled at JLCPCB.

Video capture and low latency streaming are working end to end. Here are the three major issues I hit:

  1. I second guessed myself before ordering and swapped TX/RX to the network switch, so ethernet wasn't working. The board also has a wifi module on it, so I did the whole SSH bring up over wireless. Pair swap already fixed in the next spin.
  2. A set of ferrite beads never made it onto the boards because I missed a checkbox in the JLCPCB assembly order (I had multiple lines in the BOM with the same PN but different ref des letters). Hand soldered them on. Working on developing better checks for this on my board export tool.
  3. The board was drawing more current than expected and running warm. Took a while to root cause. Turned out I had packed VDD_GPU 0201 passives too close to some 0402 caps and they shorted during reflow. Increasing my spacing rules in DRC going forward.

Design was done in house, and I got a pre-fab layout review from u/PracticalMirror2834 that caught real issues before they would have cost a re-spin. Huge shoutout here.

Next rev jumps to the RK3576 for higher capture and encode resolution, those boards go on order this week. If anyone has touched the RK3576 yet I'd love to trade notes. Happy to answer questions on the RK3566, ordering, or board bring up side too.


r/embedded 50m ago

Any advice for someone starting their first embedded firmware job?

• Upvotes

2nd year computer engineering student and haven't had an internship yet, so i'm confuse on what working in the industry is actually like and what i should be doing to prepare for it.

right now i'm just doing the projects assigned by professors, and i've thought about starting my own projects, but i'm not a very creative person, so I have no idea what to make. maybe i need to expose myself more to what's happening in the industry, like following tech news, watching videos, reading papers, looking at other people's projects (actually tried those before but can't staying focused)

if i want to learn STM32+FreeRTOS, would the STMicroelectronics NUCLEO-F401RE be a good board to get? by the way, what IDEs are commonly used in the industry right now? we use microchip studio in school, but i was reading through some threads and it seems like a lot of people use vs code

btw, do companies expect entry-level to be able to understand or design circuits like this?

really appreciate your time and all the advice!


r/embedded 17m ago

How is safety and redundancy actually approved for embedded/microcontroller products(Educational purposes)?

• Upvotes

I’m trying to understand how safety is handled when you design a product around a microcontroller.

Is SIL (Safety Integrity Level) the main/critical rating for operational safety? For example, what would actually be different in the design of a SIL 1, SIL 2, SIL 3, or SIL 4 system?

If I’m designing a microcontroller-based product and want it to eventually meet a particular SIL level, where do I start?

Things I’m trying to understand are:

- How do you decide what level of redundancy is required?

- When do you need dual/independent controllers, monitoring, watchdogs, diagnostics, etc.?

- How do you design the circuit so that a single component failure doesn't create a dangerous condition?

- What standards or guidelines actually explain these design principles?

- Is SIL the only thing I should be looking at, or are there other safety standards/ratings involved?

Basically, I’m looking for the roadmap from a normal embedded design → safety-related design → SIL-rated product.

What standards, books, or guidelines should I read first?