Global Variables vs. Safe Software
10,000 global variables is a striking code smell, and Stephen Friederichs uses the Toyota unintended-acceleration case to show why. He argues the issue was not mere coding style but absent processes: poor testing, lack of enforced standards, incorrect stack analysis, and unowned autogenerated code. Read this for a practical take on why globals and weak processes in safety-critical systems are a much bigger danger than style debates suggest.
Coding Step 4 - Design
Good embedded software design is about more than making code work, it is about making it readable, reusable, testable, debuggable, robust, and efficient. In this installment of the Coding Step series, Stephen Friederichs uses an AVR-based “Hello World” example to show how those goals shape naming, file structure, UART buffering, watchdog use, and heartbeat LEDs. The result is a practical design walkthrough that turns style and architecture choices into engineering advantages.
Practical CRCs for Embedded Systems
Stephen Friederichs shows a practical way to get correct CRC code quickly by using PyCRC to generate C implementations, then verifying them on the desktop and an AVR ATMega328P. The post walks through the common generation algorithms, how to self-test with the standard "123456789" check value, and a real timing comparison that exposes the speed versus memory tradeoffs for embedded systems.
Coding Step 3 - High-Level Requirements
Stephen Friederichs turns the series toward embedded code by showing how to write a single high-level requirement for an embedded Hello World. He explains when requirements pay off, how they support testing and scope control, and why you should not write them for every small script. He then lays out five practical rules and applies them to a concrete EHW-001 serial transmission requirement.
Dark Corners of C - The Comma Operator
Ever seen a line like if (!dry_run && ((stdout_closed = true), close_stream(stdout) != 0)) and wondered what that comma means? Stephen Friederichs unpacks the rarely-discussed C comma operator, shows a circular-buffer example where it seemed to simplify looping, then demonstrates how precedence and readability problems (and even MISRA C bans) make it dangerous in practice. Read on for practical uses and cautionary lessons.
Coding Step 2 - Source Control
Version control felt unnecessary to Stephen Friederichs when he was starting out, but this article shows why Git quickly becomes essential even for solo firmware work. He walks through installing Git on Windows, creating a repository for a simple Hello World project, making the first commit, and using reset to recover from a broken build. The post also captures a few early habits that save a lot of pain later, like committing often and keeping important files under source control.
Coding Step 1 - Hello World and Makefiles
Stephen Friederichs walks through compiling a C Hello World using GCC on Windows, then shows how a simple makefile can automate the process. You will see how output naming, project layout, and makefile targets work, and learn dependency rules based on timestamps plus how to force rebuilds with clean and FORCE targets. This is a practical first step to escape the IDE and use Unix-style build tools.
Coding - Step 0: Setting Up a Development Environment
Stephen Friederichs walks through setting up a minimal C development environment without an IDE, focusing on Windows. He explains why learning command-line toolchains matters, recommends GCC and Make as a durable base, and gives step-by-step MinGW installation and PATH configuration plus editor suggestions. The guide gets you compiling with mingw32-make and gcc so you can move on to makefiles and project structure.
Project Directory Organization
A tidy project tree can make a bigger difference than you might think. Stephen Friederichs lays out a practical directory scheme for small software projects, using familiar folders like src, obj, bin, test, reports, docs, and conf to keep builds, tests, and documentation from turning into a mess. He also explains why the root directory should welcome contributors, not confuse them.
Short Circuit Execution vs. Unit Testing
Short-circuit evaluation in C can make perfectly logical code behave differently than you expect, especially during unit testing. Stephen Friederichs walks through a simple if statement where a conditional function is never called because of short-circuiting, causing surprising test failures and hidden side effects. He shows why stubs reveal the issue and recommends using a temporary variable to ensure the call always occurs.
Implementing State Machines
Stephen walks through a practical state machine example using a dish-washing analogy to expose common implementation pitfalls and fixes. Starting from a straightforward superloop design he shows how blocking loops, global state, and interrupt races can break behavior, then refactors the code to use scoped enums, non-blocking state actions, and a simple interrupt flag to make embedded state machines safer and more maintainable.
Practical CRCs for Embedded Systems
Stephen Friederichs shows a practical way to get correct CRC code quickly by using PyCRC to generate C implementations, then verifying them on the desktop and an AVR ATMega328P. The post walks through the common generation algorithms, how to self-test with the standard "123456789" check value, and a real timing comparison that exposes the speed versus memory tradeoffs for embedded systems.
Endianness and Serial Communication
A single wrong byte order can cost you a day of debugging, and Stephen Friederichs walks through how to avoid that when sending multi-byte data over a byte-oriented serial link. He demonstrates an ATmega328P sending 16-bit ADC readings, capturing raw bytes with RealTerm, and plotting with Octave, showing how swapped endianness can produce plausible but incorrect results. The post gives practical steps to capture, test, and verify byte order.
Code Metrics - SLOC Count
Metrics and SLOC can trigger flashbacks for experienced engineers, but counting source lines of code still has practical uses when applied sensibly. This post clarifies physical versus logical lines in C, explains how SLOC can be misused to judge developer productivity, and shows how to run cloc to produce accurate per-file SLOC reports for estimation and codebase analysis.
Project Directory Organization
A tidy project tree can make a bigger difference than you might think. Stephen Friederichs lays out a practical directory scheme for small software projects, using familiar folders like src, obj, bin, test, reports, docs, and conf to keep builds, tests, and documentation from turning into a mess. He also explains why the root directory should welcome contributors, not confuse them.
Unit Tests for Embedded Code
Unit tests are one of the most effective ways to catch logic bugs early and protect embedded firmware against regressions. Stephen Friederichs explains why unit testing matters for microcontroller code, when to test, and the trade-offs between on-target and hosted approaches, with practical advice on stubbing, using the Check framework, simulators, and coverage tools to make testing realistic for embedded projects.
Global Variables vs. Safe Software
10,000 global variables is a striking code smell, and Stephen Friederichs uses the Toyota unintended-acceleration case to show why. He argues the issue was not mere coding style but absent processes: poor testing, lack of enforced standards, incorrect stack analysis, and unowned autogenerated code. Read this for a practical take on why globals and weak processes in safety-critical systems are a much bigger danger than style debates suggest.
Dark Corners of C - The Comma Operator
Ever seen a line like if (!dry_run && ((stdout_closed = true), close_stream(stdout) != 0)) and wondered what that comma means? Stephen Friederichs unpacks the rarely-discussed C comma operator, shows a circular-buffer example where it seemed to simplify looping, then demonstrates how precedence and readability problems (and even MISRA C bans) make it dangerous in practice. Read on for practical uses and cautionary lessons.
Coding Step 1 - Hello World and Makefiles
Stephen Friederichs walks through compiling a C Hello World using GCC on Windows, then shows how a simple makefile can automate the process. You will see how output naming, project layout, and makefile targets work, and learn dependency rules based on timestamps plus how to force rebuilds with clean and FORCE targets. This is a practical first step to escape the IDE and use Unix-style build tools.
Coding Step 3 - High-Level Requirements
Stephen Friederichs turns the series toward embedded code by showing how to write a single high-level requirement for an embedded Hello World. He explains when requirements pay off, how they support testing and scope control, and why you should not write them for every small script. He then lays out five practical rules and applies them to a concrete EHW-001 serial transmission requirement.
Endianness and Serial Communication
A single wrong byte order can cost you a day of debugging, and Stephen Friederichs walks through how to avoid that when sending multi-byte data over a byte-oriented serial link. He demonstrates an ATmega328P sending 16-bit ADC readings, capturing raw bytes with RealTerm, and plotting with Octave, showing how swapped endianness can produce plausible but incorrect results. The post gives practical steps to capture, test, and verify byte order.
Data Hiding in C
You can get C++-style data hiding in plain ANSI C, Stephen Friederichs demonstrates how with a FIFO stack example. He shows opaque pointer typedefs to hide struct layouts, const-qualified handles to catch accidental writes, static file-local functions for private helpers, and a canary field to detect tampering. The pattern keeps the public header stable while letting you change implementations behind the scenes.
Implementing State Machines
Stephen walks through a practical state machine example using a dish-washing analogy to expose common implementation pitfalls and fixes. Starting from a straightforward superloop design he shows how blocking loops, global state, and interrupt races can break behavior, then refactors the code to use scoped enums, non-blocking state actions, and a simple interrupt flag to make embedded state machines safer and more maintainable.
Unit Tests for Embedded Code
Unit tests are one of the most effective ways to catch logic bugs early and protect embedded firmware against regressions. Stephen Friederichs explains why unit testing matters for microcontroller code, when to test, and the trade-offs between on-target and hosted approaches, with practical advice on stubbing, using the Check framework, simulators, and coverage tools to make testing realistic for embedded projects.
Coding Step 1 - Hello World and Makefiles
Stephen Friederichs walks through compiling a C Hello World using GCC on Windows, then shows how a simple makefile can automate the process. You will see how output naming, project layout, and makefile targets work, and learn dependency rules based on timestamps plus how to force rebuilds with clean and FORCE targets. This is a practical first step to escape the IDE and use Unix-style build tools.
Practical CRCs for Embedded Systems
Stephen Friederichs shows a practical way to get correct CRC code quickly by using PyCRC to generate C implementations, then verifying them on the desktop and an AVR ATMega328P. The post walks through the common generation algorithms, how to self-test with the standard "123456789" check value, and a real timing comparison that exposes the speed versus memory tradeoffs for embedded systems.
Coding - Step 0: Setting Up a Development Environment
Stephen Friederichs walks through setting up a minimal C development environment without an IDE, focusing on Windows. He explains why learning command-line toolchains matters, recommends GCC and Make as a durable base, and gives step-by-step MinGW installation and PATH configuration plus editor suggestions. The guide gets you compiling with mingw32-make and gcc so you can move on to makefiles and project structure.
Coding Step 2 - Source Control
Version control felt unnecessary to Stephen Friederichs when he was starting out, but this article shows why Git quickly becomes essential even for solo firmware work. He walks through installing Git on Windows, creating a repository for a simple Hello World project, making the first commit, and using reset to recover from a broken build. The post also captures a few early habits that save a lot of pain later, like committing often and keeping important files under source control.
Code Metrics - SLOC Count
Metrics and SLOC can trigger flashbacks for experienced engineers, but counting source lines of code still has practical uses when applied sensibly. This post clarifies physical versus logical lines in C, explains how SLOC can be misused to judge developer productivity, and shows how to run cloc to produce accurate per-file SLOC reports for estimation and codebase analysis.
Coding Step 3 - High-Level Requirements
Stephen Friederichs turns the series toward embedded code by showing how to write a single high-level requirement for an embedded Hello World. He explains when requirements pay off, how they support testing and scope control, and why you should not write them for every small script. He then lays out five practical rules and applies them to a concrete EHW-001 serial transmission requirement.






