Claude Code vs Codex in embedded. I checked whether AI has really replaced programmers.

Under every ad for my STM32 course, someone has to pop up with the same line: “why would anyone need programming courses when AI already writes everything”. Or a variant: “embedded is over, vibe coding wins”. Most often it’s posted by someone anonymous with two posts on their account and absolute self-confidence.

Alright then. Let’s check.

I even asked my friends at software houses whether they still hire programmers at all, or whether it’s just AI sitting in the chair and coding. Because judging by what you can read in the comments, programmers should de facto no longer exist.

I decided to do it properly and live. I sat down with Claude Code from Anthropic and Codex from OpenAI and told them to write an STM32 project for me. Two hours. Two tools. One task. Below I describe everything that came out of it, including the places where they fell flat, where they surprised me and where they behaved better than I expected.

This whole post is based on the experience and observations the viewers and I gathered together during this live stream (in Polish): https://www.youtube.com/live/fDkElm7tjug

The test task

I deliberately chose a task that was realistic, but not easy for LLMs:

  • The NUCLEO-C562RE board – brand new, I’ve only just got it.
  • A BMP280 sensor over I2C – temperature and pressure.
  • Sending the measurements over UART to a terminal on the PC.
  • One measurement per second.
  • A blocking version (the simplest possible, so the AI wouldn’t break a sweat).
  • The new HAL2 and CubeMX2 from ST.

And here’s the catch. HAL2 and CubeMX2 are a fresh tool. The full HAL2 code isn’t publicly available on GitHub yet (ST is working on making it public, but for now the code is unavailable). So the models had nothing to train on. At most they could guess by analogy with HAL1.

The BMP280, on the other hand, is a classic – there are a billion code examples for this sensor on the Internet. In my course I’ve ploughed through it in every possible way. There’s so much material that an LLM should know it like the back of its hand.

So: a completely new API + an incredibly well-known sensor. A perfect test to see where AI does great and where it starts to confabulate.

Part one: Claude Code

Claude Code from Anthropic is a tool that runs in the terminal or as a VS Code extension. I chose to run it in the terminal inside VS Code – as it turned out later, that was a good decision.

Plan mode – the first pleasant surprise

Claude has three working modes:

  • Plan mode – it plans, but doesn’t make any changes.
  • Default mode – it asks for permission before every action.
  • Auto-accept / YOLO – it goes on its own, without asking.

I started with plan mode. I gave it the task, added that we were working on the new HAL2 and CubeMX2, and that we wanted the first version to be blocking. I hit Enter.

And this is where I immediately liked Claude. It didn’t charge in blind. It asked a few sensible questions:

  1. BMP280 – I2C or SPI?
  2. Peripheral configuration – in CubeMX or manually in code?
  3. Data format on UART – human readable, CSV, or both?
  4. How often do we send measurements?
  5. Do we want an interactive terminal?

I typed in the answers. And off it went.

Two agents in parallel

This is where it pleasantly surprised me. Claude launched two sub-agents in parallel:

  • One examined the project structure generated by CubeMX2.
  • The other looked for references to BMP in the codebase.

This is a feature I knew about in theory (I’d read that you can build an “agent orchestra”), but I didn’t expect Claude to decide on its own to run parallel exploration on such a seemingly simple project. It does this to save time. Very nice 👌

Along the way it spotted something that surprised me: the new CubeMX2 generates some aliases without a prefix, e.g. hi2c1. So differently from HAL1. How did it know that? Either it examined the generated files, or it actually already knows something about the new Cube. Or it has already managed to learn it somewhere. Or it’s half guessing, half working.

The CLAUDE.md file – documenting conventions

Claude generated a CLAUDE.md file for the project. It’s a kind of documentation that tells it how to behave in this particular project. It put in there:

  • Naming conventions (picked up from analysing the CubeMX2 code).
  • Directory layout.
  • V1 style – blocking, simplicity over elegance.
  • No FreeRTOS, no DMA, no interrupts in the first version.
  • “Magic numbers only where unavoidable”.
  • “Comments only where the ‘why’ is non-obvious”.
  • BMP power management – forced mode instead of normal. 👉 I think it mixed this one up with a different sensor, but OK.

So it analysed the project by itself, proposed conventions by itself and saved them to memory for the next iterations by itself. That makes a lot of sense, because in later sessions I don’t have to explain to it from scratch how it should work. I just need to drop “stick to the assumptions in CLAUDE.md” into the prompt.

Confabulation – the first slip-up

Everything was going great until we got to GPIO. Claude told me that the LED GPIO was configured as an output. But when generating the project I had accidentally got it wrong and it was set as an input. I didn’t even know I’d got it wrong – only the viewers in the chat pointed it out to me 😇

I ask it straight out: where did that output come from?

And it tells me: “I guessed from memory based on the name LD1 alone”

It apologised very nicely. I gave it a good flogging and it promised it wouldn’t make assumptions based on names any more. It even saved that to the project memory.

But the moment this happened is crucial for this whole experiment. If I hadn’t known myself how I’d configured that peripheral, we would have carried on with the bug. It would have compiled, been flashed to the board, the LED wouldn’t light up, and I’d be sitting for two hours with a logic analyser wondering where the problem was.

An LED is no big deal. But what if it were, say, a motor driver? Would we have sent a short circuit into a high-current load? That would be bad, wouldn’t it?

An LLM will always tend to fill gaps in its knowledge with guesses. It’s a trait of these models, not an implementation bug in a particular tool. And in embedded it bites especially hard, because here a small mistake (e.g. in a pin configuration) often means hours of debugging.

What Claude wrote in the code

After planning, Claude moved on to writing. And it went all in. It didn’t go step by step, it didn’t verify as it went. It simply wrote everything at once:

  • log.h and log.c – a printf-like wrapper over UART, using vsnprintf and an argument list (va_list).
  • bmp.h and bmp.c – a full structure with all 12 BMP280 calibration coefficients, init, read, temperature and pressure compensation (those damn datasheet formulas), and handling of the I2C address shifted to 8 bits.
  • Changes to main.c – integration, a while loop reading once per second.
  • An update to CMakeLists.txt – adding the new files to the build.

Everything object-style (a struct for the BMP, init status, etc.). On HAL, as usual. Everything looked good, but to be honest, I suspect it isn’t anything complicated that Claude came up with on its own – there’s so much BMP280 code on the Internet that it probably just took a typical pattern.

Limits – the second slip-up, and this time a fatal one

And this is where the brutal reality of working with LLMs caught up with us.

I was on the Pro plan for 20 dollars a month. Model: Opus 4.6 (the most advanced at the time – 4.7 had already come out). Effort: xhigh (the highest possible reasoning).

After about an hour of work I’d eaten through the whole daily limit. Reset in another two hours. 16% of the weekly limit gone in one short session.

The live project ended not because the project was finished, but because Claude stopped answering me. The code never made it onto the board. I knew the token allowance was small, but that small?

This is something nobody writes about in those beautiful marketing and clickbait videos about “replacing programmers”. 20 dollars a month is definitely not a plan for commercial work. It’s a plan for “try it out and see”. Real work with Claude Code requires the Max plan for 100 dollars a month, and probably even higher (because there are two Max tiers), and often buying extra usage on top.

The second thing: these tools are token-hungry. They do everything they can to hit your limit as fast as possible. Every git status, every diff check, every “let me think about it” – all of that is burned tokens. You have to factor it in. There are probably ways to limit it, and we’ll discover them together on my next lives.

Claude Code summary

What it does well:

  • Plan mode with follow-up questions about details – sensible and no nonsense.
  • Parallel exploration of the project with sub-agents.
  • Generating a CLAUDE.md file with conventions – analysing the project on its own and writing down the rules.
  • Working with Git – it proposes commits and makes them itself after approval.
  • It understands CMake – it figured out that files have to be added to the build manually.
  • It writes whole modules straight away, well organised (folders, h+c, structs, statuses).

What it messed up:

  • Confabulation – it assumed something from a variable name instead of checking the file.
  • It went all in – everything at once, no verification in stages.
  • It burned the whole daily limit in an hour of work on one sensor.

Part two: Codex (OpenAI)

So as not to end the stream after less than an hour, the viewers suggested trying Claude Code’s biggest competitor. So I switched to Codex from OpenAI. It was the first time in my life I’d opened it (I’d already tested Claude a bit before), so here I was learning on the fly together with you.

I installed Codex as a dedicated VS Code extension. That’s the first structural difference: Claude ran in the terminal, Codex in an extension. As it turned out later, that had consequences.

A step-by-step approach

Codex behaved exactly the way I run my STM32 programming courses.

By the way – a moment of self-promotion – my courses (in Polish):

I gave it the same task as Claude. It started thinking. And then, instead of going all in, it suggested on its own:

Let’s do a proof of concept first – read only the Chip ID from the BMP to verify whether I2C communication works at all. Only then will we add the rest.

That’s exactly the method I teach in my courses. First check that you can communicate, only then add the rest. Because if something doesn’t work after a full implementation, you don’t know whether the problem is in the communication, in reading the calibration, in the calculations or in sending over UART. Going step by step, you have a chance to eliminate most of the problems.

Why did Codex do it this way and Claude didn’t? My theory: Codex was probably trained on sources that go exactly like this. Embedded tutorials often have this structure – chip ID first, then the rest. Claude behaved like someone who has seen a lot of code and can reproduce all of it from memory. Codex behaved like someone who has seen a lot of tutorials and can walk us through them.

More talkative

Codex explained what it was doing and why. Claude stays quieter and just works; Codex is more of a narrator here.

In its plan it listed, among other things, fresh sources from the ST Community forum from a month earlier, which I didn’t even know about. I didn’t specifically tell it to look for current discussions – it proudly showed them off itself in the “reference sources” section of the plan. And indeed, those ST blog posts were about the current HAL2 and how to use UART and I2C. Brilliant!

An important caveat: in the transcript you can see that Codex listed those sources in the plan. Whether it actually used them or only linked them – I don’t know with 100% certainty. But just the information that that’s a place worth searching was valuable to me.

Where it writes files

Here I caught a difference between Claude and Codex.

Claude wrote into the user_modifiable folder – in line with the convention by which CubeMX2 separates user code from generated code. Or at least I think that’s the folder for user code, because I don’t know these new things well yet.

Codex wrote directly into the project root folder, next to the generated folders. I commented on it during the live: “it’s writing the application right here instead of putting it in folders”. I don’t know whether that’s a mistake or a deliberate decision by Codex. Maybe it isn’t aware of the new CubeMX2 convention and writes “the old way”. Or you have to state it clearly in the project assumptions.

Nevertheless, this way the “user code vs generated code” separation gets lost. In practice: when you regenerate the project from CubeMX2, you have to pull your code out of the files manually so it doesn’t get overwritten. For now we don’t know yet how well the mechanism for “not overwriting” user code works. ST has dropped the *USER CODE BEGIN/END* markers.

Codex and Git – a problem

This is where Codex was knocked flat on its back.

It tried to make a commit. It checked the status. It ran a diff. It checked the Git config. It checked git log –oneline. It checked head, tail -5. And round and round. It got stuck in a loop. It couldn’t make a commit, something Claude had no problem with at all.

To be honest, I don’t know exactly what didn’t work there. Possible explanations:

  • PowerShell – I’m on Windows, and the terminal in VS Code launched PowerShell by default. Codex tried git commands, but something wasn’t working for it.
  • No terminal – Claude ran in the terminal and had direct access to git. Codex as a VS Code extension – not quite the same.
  • Sandbox – Codex asked me to “setup agent sandbox”. It’s possible that its sandbox somehow isolates it from Git.

A suggestion from your chat: WSL. Or: switch to a regular terminal. I didn’t test that on the live, but I’ll definitely do it on the next one.

In practice, it ended with me running the git commands myself in Git Bash, while Codex only suggested them to me. So the exact opposite of what was supposed to happen.

Codex and hardware – the second slip-up

Codex tried to run the code on a board it had no physical way to touch, because it wasn’t connected 🥲

First it did a build. Then it tried to configure a debug preset. Then it tried to flash something. And round and round. It didn’t let me know that something was wrong. It was burning tokens, which is what it loves.

I had to tell it straight: “you only write the code, I run it”. Otherwise it would have kept trying.

That’s an interesting difference. Claude didn’t try to run anything – it simply waited. Codex with “full access” permissions by default (which you have to set yourself in the sandbox) is very proactive. If you don’t tell it “stop”, it tries everything, including things it shouldn’t.

The BMP doesn’t respond – debugging with AI

After flashing the code to the board we got: “BMP not found”.

A classic. The sensor isn’t responding. Here Codex itself suggested a few likely causes:

  • How the address is passed (shifting 7-bit to 8-bit).
  • I2C configuration.
  • Power supply.
  • Wiring.

I checked. Wiring OK. Power OK. So the problem was in the communication or the address.

A suggestion from your chat: add an I2C scanner, and we’ll have 100% certainty instead of guessing. Genius, thanks!

When asked, Codex added the scanner very efficiently. We flashed it. The scanner found the device at a different address than Codex had assumed. The BMP280’s default address depends on the state of the SDO pin – with SDO tied to GND it’s one address, with it tied to VCC it’s another. Codex didn’t ask about this configuration, it just guessed the default.

After the fix – temperature and pressure appeared in the terminal. They reacted to touch. The values changed. So, task done.

But note: it worked only because I knew (or rather, the chat and I knew together) how the BMP280 works and how to diagnose it. Without that domain knowledge, “BMP not found” would have stayed forever, because we’d have run out of tokens. Codex’s plan was to keep swapping address values and trying to run the firmware again.

Codex summary

What it does well:

  • Step-by-step approach – a proof of concept before the full implementation.
  • Talkative in its explanations – it explains what it does and why.
  • It lists fresh sources in the plan (from industry forums).
  • Diagnostics – it sensibly lists likely causes of problems.
  • After a suggestion (the I2C scanner) it reacts quickly and adds helper code.

What it messed up:

  • Git loop – it couldn’t successfully make a commit in PowerShell.
  • It tried to flash the board by itself, even though it wasn’t connected.
  • It writes files in the bare root folder, losing the user/generated separation.
  • It didn’t ask about the BMP280 I2C address – it guessed the default.
  • It ate 96% of the weekly limit in one session (GPT-5 model with reasoning high).

The key differences between Claude and Codex

After two hours of working with both tools, I can see a few important differences.

Working style

Claude: fast, all in, all the code at once, debugging later.

Codex: step by step, verifies after every step, waits for a response before moving on.

These are two different philosophies. Claude is more like a senior who knows what he’s doing and writes the whole module in one go. Codex is more like a junior who wants to make sure he isn’t writing anything unnecessary.

In practice: if you want to build a new project from scratch, Claude is faster. If you have existing code where something needs fixing or extending step by step, Codex is safer.

This observation matches a comment made during the live: “Codex is good for fixing bugs and working on existing code, Claude is great for creating a project from scratch.” After two hours I can confirm that, for now at least. But I’ll keep investigating 🙂

Communication

Claude: quieter. It works, sometimes writes what it’s doing, but you often have to ask it “BTW, what are you doing?” if you want to find out what it’s up to. Of course, during planning there’s a lot of conversation with it first, but after that – it just works.

Codex: chattier the whole time. It explains every step, writes “I’m doing this because…”, “now I’m moving on to…”. It presents it a bit less clearly, but it does it.

Codex is more pleasant to work with, because you can see what’s going on. Claude is faster, because it doesn’t waste tokens on narration (but it still ate all of them before finishing the project 😅).

Working with external tools (Git, build, flash)

Claude: works great in the terminal. Git, CMake, build – it handles everything. It asks for access to system commands. You can grant permission permanently, or ask it to check with you every time.

Codex: in the VS Code extension it gets stuck in a loop with Git in PowerShell. It tries to flash boards that aren’t there by itself. I’ll try to dig deeper into this – it has to work on its own.

This is a big minus for Codex for people on Windows. A suggestion from the chat: WSL or a regular terminal.

Asking for permission

Claude: by default it asks for permission before every action (in default mode).

Codex: in a sandbox with full access it goes on its own, without asking. You have to tell it clearly what not to do, sometimes several times.

For people who want control – Claude. For people who just want the result – Codex.

Limits

Claude Pro $20: about an hour of work on Opus with xhigh effort. Daily limit, bye-bye. Sad.

Codex (ChatGPT Plus plan, probably with reasoning high): 96% of the weekly limit in one session. Project completed successfully.

Both tools are token-hungry. You have to choose the model and effort sensibly. Sometimes Sonnet (Claude) or o5-mini (Codex) is enough.

What does this mean for us embedded folks?

After two hours of work I have a few general conclusions that go beyond comparing the tools themselves.

Domain knowledge is still a must

Throughout the whole two hours I was correcting the AI every few minutes on average:

  • The right pins on the Nucleo schematic (Claude proposed the defaults, I verified them on the schematic).
  • Verifying whether the GPIO is an input or an output (Claude’s confabulation).
  • The right I2C mode and speed.
  • Spotting the problem with the BMP280 address (which depends on SDO).
  • The decision that I flash the board, not the AI.
  • The decision that we don’t use DMA / interrupts in the first version.

If I hadn’t had that knowledge, the project wouldn’t have worked. And worse – I wouldn’t have known why. The AI would have guessed something that “should work” but in practice doesn’t.

Vibe coding in embedded is a road to a project that “builds” and probably doesn’t work on the hardware. You need embedded knowledge. Nothing has replaced me here yet.

Embedded is 10 years behind web development

In web development someone types “build me a shop” and gets a shop. I work like that myself 😅 The frameworks are public, documentation is everywhere, there are millions of code examples on GitHub.

In embedded you type “read the BMP280” and get something that should theoretically work, but the sensor stays silent because the address is different. At least that’s what my experience so far suggests.

Why? Because:

  • Reference manuals are 3000-page PDFs that don’t fit in the AI’s context.
  • There’s less code on GitHub, and it’s fragmentary and often doesn’t work.
  • Every board, every chip, every peripheral has its own quirks.
  • New tools (HAL2, CubeMX2) aren’t well known yet.

That protects us from AI, but it’s also a dead end if you want to get into embedded today as a vibe coder. Because there simply isn’t enough material for AI to learn well from. But that will change over time – that’s more than certain.

Limits are a real cost

Claude’s $20 Pro plan is an hour of work on one sensor. The ChatGPT Plus plan isn’t much better.

Real commercial work means:

  • Claude’s Max plan for $100 a month or more.
  • Buying extra usage (Codex supports it, so does Claude).
  • Thoughtful context management – cutting out folders the AI shouldn’t read (e.g. CMSIS, other Cortex architectures).
  • Choosing the model and effort for the specific task – you don’t always need the strongest Opus with xhigh effort.

All of this is a new skill for a programmer that has to be learnt. It’s not enough to type “write me some code” and wait. The programmer’s job isn’t being eliminated – it’s just changing.

Context is king

A few things I noticed in this direction:

  • CLAUDE.md – a file with project conventions that the AI refers to.
  • context7 – a Claude Code plugin that pulls in documentation (I haven’t tested it myself yet, but I read nothing but good things about it).
  • Converting PDFs to markdown – because markdown works better as context for an LLM than PDF.
  • Cutting out unnecessary folders in the project, so the AI doesn’t wander through them and burn tokens.
  • Skills and plugins – the ecosystem around Claude and Codex is growing fast.

The better the context you give the AI, the better the code it writes. Without context it’s shooting blind. I also have to stress clearly that on this live we went in on the assumption that we knew absolutely nothing. Well… sometimes we did have to step in using our embedded expertise 🙂

My vision of what comes next

This is me speculating – you can hold me to it in 3 years.

It seems to me that the agents we have now – Claude Code, Codex and all the rest – are the first iteration. They all run remotely, everything goes to the cloud. And that will change, because you can see exactly the same trend we saw in embedded.

At Embedded World over the last few years you could clearly see cloud AI moving to Edge AI – that is, everything running locally, on a small microcontroller. In my opinion, programming agents will go the same way.

Why? Because the whole enterprise segment cares about its IP, its know-how, its privacy. Some care more, some less, but at least they say they care. And they will push for tools like these to be available locally, hosted on their own servers.

My bet is that it will come in two waves:

  1. First, large models on beefy servers that companies will host themselves. Small local models like Google’s Gemma 4 are already starting to appear. We also have a few models from OpenAI, but above all hundreds of open-source models ready to work straight away and for free. That’s the first step.
  2. Then small models specialised in programming that we’ll run on an ordinary PC. Less power, but they’ll cope. And that will be a breakthrough for people who don’t want to send their code to the cloud – especially someone else’s.

In other words, Edge Agent AI. Just as embedded went towards Edge AI, agents will go towards Edge Agent AI. That’s my speculation.

And here’s the second thing: the programmer’s job will change, but it won’t disappear. My bet is that it definitely won’t in the next 5 years.

How long has AI been with us? The third year? For three years everyone has been saying that programmers are finished. And? They’ve been finished for three years, and yet they’re still among us.

It’s a bit like going from an abacus to a PC. Except you have to know how to operate the PC.

What we do next

I’ll keep pursuing this topic. On the next lives I’m going back to the BMP280 project – we still have an interrupt-driven version planned, then a DMA one. With Claude and Codex. We’ll check:

  • How they cope with more complex code (DMA isn’t blocking).
  • What the AI can propose on its own, and what you have to tell it explicitly.
  • How to configure agents so they don’t get tangled up (a suggestion from the chat).
  • How to work with context7, skills, MCP.
  • Whether we can give them the reference manual (converted to markdown).
  • How to save tokens.

Plus I also want to try doing the same thing with a less popular sensor. Because the BMP280 is a classic – there’s a billion code examples online. I’m curious how AI will cope with something that isn’t in its dataset.

There are many interesting hours of discovery ahead of us. I hope you’ll join me.

👉 And one more thing – I’ve set up a dedicated “AI w Embedded” Discord server. So that we can share experiences live, and not once every two weeks. The topic is broad, it changes fast (every week a new model, a new tool) and one person can’t keep up with it alone.

If you’re also testing these tools in your projects – drop by, share what works and what drives you mad.

Join here: https://linki.msalamon.pl/ai_embedded

So has it replaced us or not?

In the poll at the end of the live, 90% of 60 people voted that AI hasn’t replaced us yet.

Strange, because the commenters under the ads think otherwise. They probably just didn’t come to the live. Or AI has replaced them in watching the guy whose comments they spam as well 🙂

My opinion after two hours with Claude Code and Codex? AI is a great assistant, but not yet a replacement. Just as the hammer didn’t replace the carpenter, only the carpenter without a hammer. The embedded programmer’s job will change – those who don’t learn these tools will find it hard to compete. But the knowledge of how hardware works, how to configure peripherals, how to debug on the hardware – all of that stays. AI won’t do it for us.

At least not in this iteration.

And that’s why we’ll keep learning it together.

Let me know in the comments how it’s going for you. Are you already using AI for embedded? What works, and what annoys you? Drop by the Discord. Sign up for the next live.

See you live ✌️

Podobne artykuły

.
Categories: STM32

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *