DEV Community

Pranav Jain
Pranav Jain

Posted on Originally published at pranavhj.github.io

AI Code Assistants for Embedded Engineers: What Works, What Doesn

I write C for microcontrollers. My code talks to SPI peripherals, configures DMA channels, and runs in environments where a buffer overflow doesn't crash a browser — it crashes a piece of industrial equipment. AI code assistants were not built for this.

But I use them every day. Here's what actually works, what's dangerous, and where I think this is heading.

What I Tested

I've been using three AI assistants in my embedded workflow for the past year:

  • GitHub Copilot — inline completions in VS Code
  • Claude — long-form code generation, architecture discussions, code review
  • ChatGPT — quick questions, datasheet interpretation

I used them on real firmware projects: STM32F4, nRF52840, ESP32-S3. Bare-metal and Zephyr RTOS. 20-60K LOC codebases. Here's what happened.

Where AI Assistants Actually Help

1. Boilerplate Peripheral Initialization

This is the clearest win. Peripheral init code is formulaic — read the reference manual, fill in the struct fields, enable the clocks, configure the pins. AI assistants are very good at this.

Example: "Write STM32 HAL SPI1 init for PA5/PA6/PA7, Mode 0, 1 MHz, 8-bit"

Claude gives me:

void SPI1_Init(void) {
    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef gpio = {0};
    gpio.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
    gpio.Mode = GPIO_MODE_AF_PP;
    gpio.Pull = GPIO_NOPULL;
    gpio.Speed = GPIO_SPEED_FREQ_LOW;
    gpio.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &gpio);

    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    HAL_SPI_Init(&hspi1);
}
Enter fullscreen mode Exit fullscreen mode

This is correct, took 5 seconds, and would have taken me 10 minutes of cross-referencing the reference manual. The prescaler calculation (64 MHz / 64 = 1 MHz) is right. The AF mapping (AF5 for SPI1 on PA5-7) is right.

Time saved: 10 minutes per peripheral init. Maybe 30-40 minutes per project.

2. Protocol Decoders and Data Parsers

Parsing sensor data, decoding protocol frames, bit manipulation — AI assistants handle this well because the logic is well-defined and the edge cases are enumerable.

Example: "Parse this BME280 temperature reading from raw registers"

int32_t bme280_compensate_temperature(int32_t adc_T,
                                       struct bme280_calib *cal) {
    int32_t var1 = ((((adc_T >> 3) - ((int32_t)cal->dig_T1 << 1))) *
                    ((int32_t)cal->dig_T2)) >> 11;
    int32_t var2 = (((((adc_T >> 4) - ((int32_t)cal->dig_T1)) *
                      ((adc_T >> 4) - ((int32_t)cal->dig_T1))) >> 12) *
                    ((int32_t)cal->dig_T3)) >> 14;
    return (var1 + var2);
}
Enter fullscreen mode Exit fullscreen mode

This is directly from the BME280 datasheet compensation formula. AI gets it right because it's seen this exact code in thousands of projects.

3. Test Scaffolding

AI is good at generating test cases for embedded code — especially when you give it the function signature and describe the edge cases.

"Write Unity test cases for this CRC-16 function, including: empty input, single byte, known test vector, maximum length buffer."

This saves 15-20 minutes per function and produces tests I might not have thought of.

4. Build System Configuration

CMake for cross-compilation is arcane. AI assistants know the incantations:

"Generate a CMake toolchain file for ARM GCC targeting Cortex-M4 with FPU"

This consistently produces working output. CMake is well-documented online and AI has seen thousands of examples.

5. Documentation and Comments

"Add doxygen comments to this driver interface header" — AI does this well. It reads the parameter names, infers the purpose, and produces reasonable documentation.

Where AI Assistants Are Dangerous

1. Register-Level Code for Uncommon Peripherals

Ask for LTDC (LCD controller) configuration on STM32F4 and you'll get plausible-looking code that doesn't work. The AI has seen fewer examples of LTDC than SPI, so it generates something that looks right but has wrong timing parameters or missing register fields.

Rule: If the peripheral has fewer than 1000 open-source code examples on GitHub, don't trust AI-generated register-level code without verifying against the reference manual.

2. DMA Configuration

This is where I've seen the most AI-generated bugs. DMA involves channel assignment, priority, FIFO thresholds, memory alignment, and peripheral-specific constraints. AI gets the structure right but misses constraints like "DMA2 Stream 0 Channel 3 is the only valid assignment for SPI1 RX on this specific STM32 variant."

// AI-generated DMA config — looks correct but...
hdma.Init.Channel = DMA_CHANNEL_3;  // Wrong channel for this peripheral
hdma.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma.Init.PeriphInc = DMA_PINC_DISABLE;
hdma.Init.MemInc = DMA_MINC_ENABLE;
hdma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;  // Bad choice for small transfers
Enter fullscreen mode Exit fullscreen mode

Rule: Always verify DMA channel assignments against the DMA request mapping table in the reference manual. AI can't reliably do this.

3. Interrupt Priority and RTOS Integration

AI assistants don't understand the runtime implications of interrupt priorities. They'll generate code that assigns ISR priorities without considering what other interrupts are active, whether the RTOS uses BASEPRI masking, or what configMAX_SYSCALL_INTERRUPT_PRIORITY is set to in FreeRTOS.

4. Timing-Critical Code

Anything that depends on cycle-accurate timing — bit-banged protocols, pulse measurement, ISR latency — is a bad fit for AI assistance. The AI doesn't know your clock speed, pipeline behavior, or compiler optimization settings.

5. Security-Sensitive Code

Cryptographic operations, secure boot, key storage. Don't use AI for this. The surface area for subtle bugs is too large, and the consequences of getting it wrong are too severe.

How I Actually Use AI in My Workflow

Morning: Open the project, use Copilot for autocomplete on routine code. It fills in struct initializations, for-loop bodies, and switch-case arms.

Architecture questions: "I need SPI communication between two MCUs with flow control. What patterns work?" — I ask Claude, get 3 approaches with tradeoffs, then implement the one that fits.

Debugging: "This SPI transfer returns HAL_TIMEOUT. The clock is configured for 1 MHz, CPOL=0, CPHA=0. What should I check?" — AI gives a reasonable debugging checklist. Not always right, but a good starting point.

Code review: Paste a function into Claude and ask "What bugs or edge cases do you see?" — catches things like integer overflow in ADC scaling, missing null checks on buffer pointers, off-by-one in circular buffer indices.

What I never do: Copy-paste AI-generated code into production without reading every line. Especially register-level code. Especially DMA.

The Embedded-Specific Gap

AI assistants are trained on web and application code. The embedded domain has unique challenges they handle poorly:

  • Hardware datasheets are not in their training data (or poorly represented)
  • Real-time constraints aren't something they can reason about
  • Memory-constrained environments mean patterns that work in application code (dynamic allocation, string formatting) are wrong in embedded
  • Vendor-specific errata — every MCU has hardware bugs documented in errata sheets. AI doesn't know about them.

The ideal embedded AI assistant would:

  1. Have the MCU's reference manual in context
  2. Know the specific chip variant and its errata
  3. Understand RTOS-specific constraints (stack sizes, priority inversions)
  4. Verify DMA channel assignments against the mapping table
  5. Flag timing assumptions that depend on clock configuration

We're not there yet. But we're closer than we were a year ago.

Bottom Line

AI code assistants save me 30-60 minutes per day on embedded projects. Mostly from boilerplate generation, test scaffolding, and build system configuration.

They produce dangerous output for DMA, interrupt priorities, and uncommon peripherals. The cost of a subtle register-level bug in production embedded code is much higher than in a web app — hours of debugging with an oscilloscope and logic analyzer, or worse, a field failure.

Use them as a first draft generator, not a finished code source. Every line gets reviewed against the reference manual. That discipline turns AI from a liability into a genuine productivity boost.


Pranav Jain writes middleware and abstraction layers for embedded systems. Find him on GitHub.

Top comments (0)