Chapter 26 - Embedded Systems #

By Make It With Me

If you’re reading this page, it’s expected that you’re either pretty comfortable with C and programming on a normal computer already or that you’ve at least read though both the programming intro chapter starting with

“Lets Write Some Code” and the “Low Level Programming” page

What is an Embedded System? #


[TODO] need source

These little devices are Pocket Operators made by Teenage Engineering, they’re relatively inexpensive synths that can be chained together, and I think they’re a great example of what an embedded system is, but for the sake of clarity, here’s how Wikipedia defines it:

An embedded system is a computer system—a combination of a computer processor, computer memory, and input/output peripheral devices—that has a dedicated function within a larger mechanical or electronic system.

- Wikipedia

Alright, so how do these pocket operators show this well? Well, the pocket operators have a Microcontroller on them as well as a fair amount of things connects to it, but let’s hang on for a second- Microcontroller? Microcontrollers, sometimes written as μC, MCU, or just micro are the heart of most embeded projects

A microcontroller is a small computer contained in an integrated circuit (IC) chip. A microcontroller contains one or more CPUs (processor cores) along with memory and programmable input/output peripherals. Microcontrollers are designed for embedded applications, in contrast to the microprocessors used in personal computers.

Microcontrollers are used in automatically controlled products and devices, such as automobile engine control systems, implantable medical devices, remote controls, office machines, appliances, power tools, toys and other embedded systems.

- Wikipedia, minor edits

Alright, so that’s a lot of words. The gist of it is you’re getting a tiny computer that’s roughly comprable in specs to a computer from the 80’s, but is inexpensive (usually under $2.00, especially in bulk) and that has a ton of pins that you can connect other things to- LEDs, motors, buttons, etc.

These computers usually don’t run a full operating system, and instead usually just run a loop of your code forever. They also usually have some special pins that can do things like read analog voltages, communicate over SPI or \(\text{I}^2 \text{C}\) , trigger interrupts, or be used for PWM (Puse Width Modulation).

Probably the most commonly recommend microcontroller for beginners is the ATMEGA328P on the Arduino Uno. The 328P and Uno are both… not awesome. But, for first learning and getting your toes wet they’re fine.

It’s worth noting, the μC itself is the Atmega328P- the really big black rectangular chip. The board that breaks these pins out to the headers for you to plug wires into, provides the USB connection, power filtering, reset button, etc. is the Development Board- in this case it’s been named the Arduino Uno.

Image by Sahand Babali

Image by Vishnu Mohanan

Sometimes the lines for what’s a microcontroller or embedded system can get blurry. This is especially the case when using a development board on a finished product or when a full computer that runs a desktop OS is involved. For example, the Raspberry Pi, pictured here, is an embeded system; however, it has a full blow ARM System On a Chip (SoC) that can run Linux and be used as a full desktop computer.

More confusing still, the Pi itself has multiple embedded systems in the embedded system- for example the dedicated chip on the Pi3b+ that provides the Ethernet connectivity could be considered part of its own embedded system. Yet, the Pi has General Purpose Input Output (GPIO) pins like most microcontrollers.

All of this is to say, don’t worry about it too much. As you work with it more, you’ll learn the differences. This is all just jargon anyway, and sometimes not everything fits cleanly under a single label. Instead, you should focus on what matters: Knowing what parts to use, what features they offer, and how to program them to do what you want.

For the rest of this page, I’m going to be talking about embedded systems that don’t have processors beefy enough to run Linux - those are usually called “Single Board Computers” or SBC’s anyway, just because they’re really in an entirelly different weight class and price bracket. If you know how to work on the classic μC embedded systems as I’ll dicuss going forward and you know how to do general desktop development, making software for a Single Board Computer, such as the Pi, is really just a blend of the two and should come pretty naturally.

Alright, so you have a microcontroller, presumably on a dev board, maybe even aforementioned Arduino Uno (though seriously, the 328p is trash- coming back to that in a bit) but you want to make it do something. Generally, the first thing you’ll want to do with a devboard is the exact same as you do when learning any programming language, make it say “Hello World”. Now, this is pretty easy, if you have a controller that can run the Arduino framework (this is more than just boards from Arduino- Arduino the framework runs on a bunch of other boards that aren’t made by Arduino the foundation), you can download the Arduino IDE, write this code:

void setup() {
  // put your setup code here, to run once:
  Serial.println("Hello World!");

void loop() {
  // put your main code here, to run repeatedly:

Connect your board, press upload, and if you go to Tools → Serial Monitor you should see the Arduino send back the words “Hello World!”. Now, this is super not exciting, as it doesn’t really seem any different from just running similar code on your computer. However, keep in mind that code spitting out that text didn’t execute on your computer. It ran on that lil’ board, and then that text was sent back over the USB cable.

But, there are a few things I want to address first:

  1. The Arduino IDE is fucking garbage (for now, it should be getting better with Version 2.0)
  2. This code leaves a lot obscured.
    • Where is Serial from?
    • Why are we not just using printf()?
    • What’s up with setup() and loop() instead of main()?
    • Why 9600?

So, to answer 1. - Yeah, don’t use the Arduino IDE. For now, the best option is PlatformIO on VSCode.

To rapid fire 2. - Serial is from a library that gets installed by default with Arduino and by using a call to it in your code, the Arduino IDE links it in for you, without the need for #include like we saw back in the low level programming chapter. This may sound nice, but it’s actually really annoying. There are a bunch of libraries like this, and often you won’t realize you’re even using something non-standard- like setup() and loop() Arduino actually has a file sitting elsewhere that literally has the code:

#include <Arduino.h>

int main(void)

#if defined(USBCON)
    for (;;) {
        if (serialEventRun) serialEventRun();
    return 0;

which, while may look a bit weird, you’ll see does contain our typical main() and just executes setup() once then whatever we put in loop() in an infinite for loop. It also contains #include <arduino.h> which will include if you dive into you’d see includes the Serial library too.

Okay, but WTF is “Serial”? Why not just use printf()

Okay, so, there’s a few things here.

First, the “arduino” framework isn’t actually C it’s C++ and the standard way printing is done in C++ is a bit different anyway - yes, you can use printf() in normal desktop programs in C++, but you usually want to use a syntax closer to this

cout << "Hello World!";

But, more improtantly, C++ is also object oriented, so we’re calling the println() function from the Serial class - it’s an entirely different language. Again, not a big deal to think about right now. There are two take aways you need to really answer your question

  1. println is basically just a printf that add’s the newline for us
  2. Serial is a class that gives provides us functions for working with the serial interface hardware on the board

Now, 2. Is the more important and more confusing point and requires a smidgen of historical context.

You connected the board to your computer over USB or Universal Serial Bus. Prior to USB we also had … Serial. Just… Serial. USB can (with a lot of hand waving to what is actually going on) let devices show up as old school Serial devices though. If you’re on Windows, these will show up as com ports (COM4 or whatever) on Linux, they’ll show up as /dev/ttyACM0 or /dev/ttyUSB0 (with the number incrementing depending on the number of devices) - this old school serial connection is pretty damn slow but basically just gives us a way to send data (usually text) back and forth to a device - allowing us to print that data to a terminal or send data from a terminal.

This is why we can’t just use printf() - where would that text even show up?! There’s no screen on the board! no terminal. We need to send the data back to the computer over the serial interface to have a way to read it!

Finally, just to be though, that 9600 which may seem like a magic number, is the baudrate the board was sending data to the computer over Serial. 9600 is stupidly slow, yet is the default that many programs with the Arduino framework will use- most boards can do 115200, which is 12 times faster.

Really, most of this abstraction is just in our way. This isn’t C++’s fault, the Serial class providing functions like println and having encasulated functions for setup - great, that’s well made. Not including serial.h ourselves or having a convient tool for looking at how the underlying libraries tie together? That is a dumpster fire.

There’s no need for all of that to sit between us and the code!

A lot of “Makers” will only learn to use the Arduino functions and way of doing things. Yeah, they might make some things easier, but long term they’ll seriously limit what you can do. I love “Makers” and “Hackers” and consider myself one, but if you use the title as an excuse to not learn how shit works, you’re doing it wrong.

Sometimes, a quick n’ dirty solution will work. If you just want to make some LEDs blink or switch a relay to turn something on and off, sure. The problem is, sooner or later you’ll find something where you need fast response times and need to use interrupts, or where you need to get fine control over PWM and need to twiddle bits, and the Arduino library will make doing so a massive pain in the ass.

So, let’s try this again, this time, don’t use the Arduino IDE, but grab PlatformIO and make a new project.

I’ll assume that most people reading this are- despite how much of a massive pile of shit they are- using an Arduino Uno (or clone). If that’s all you have, frankly, the Arduino library is still your best bet for some things, so when you make a new project because it does provide some things that are a total pain to do by hand, like setup aforementioned Serial. That said, the other library that works in PlatfromIO for the Uno, Simba is pretty cool and will abstract out a lot of that mess too.

The biggest reason to use the Arduino framework is that there’s a mountain of libraries written for it, so if you get a little board for RFID or temperature sensing or GPS or whatever, there’s probably already a drop in library with example code. But, frankly, the 328P is so under powered and old that I just can not justify learning on it. Instead, I’m going to recommend the STM32F411CE - or, as it’s more commonly known, the “Black Pill”

If you’re buying one to follow along, be careful. There are variants of the BlackPill that have the F401 or F103. The F103 boards almost universally have issues, so at the very least make sure it’s an F401 or F411.

But, I hear you asking, “why?”

Arduino Uno/Nano (328p) BlackPill (STM32F411CE)*
Price ~$6.00 ~$6.00 + ~$7 debugger
№ Bits 8 32
Clock Speed 20Mhz 100Mhz
Program Memory 32Kb 512Kb
Ram 2Kb 128Kb
I/O Voltage 5V 3.3V (lots of 5V tolerant pins)
Debugging Kinda, but not really. With ST-Link
USB that big ol’ printer style on the UNO, or micro-usb on the Nano USB-C

*The STM32F401CE boards are a smidgen worse, but honestly, they’re both so good it probably won’t matter to you. Get whatever is cheaper.

Even if most of these specs don’t mean anything to you and whatever projects you have in mind don’t need a beefy processor, while learning espcially you don’t want to be constantly running out of RAM or room to store your code.

If that doesn’t convince you, then I don’t know what will. Oh, and the BlackPill still supports the Arduino framework, if you do actually need it. Now, to be fair, there’s a lot more to this than these specs. The 328p will almost certainly have better library support than the BlackPill, the Uno has a bunch of snap-on accessories, and some things do actually work better with the 5V i/o of the Uno. It’s just… sort of dumb to start on something so old and so far removed from modern practices.

You’d also want to consider what protocols and inputs the boards can handle. In this case, the BlackPill is basically better in every way in that front too. If you were looking at other options though, some boards may not have analog input pins, for example, and that might be a dealbreaker for some projects. I’ll talk more about how to find the right μC / Devboard for your project later, for now, let’s move on assuming you have the BlackPill in front of you.

So, let’s try making our Hello World! again.

[TODO] Blinking the onboard LED

[TODO] Using the Arduino framework in PlatformIO, then setting up STM32CubeIDE because it’s actually good. But explain that PlatformIO is still great because of support for ATmega, ESP8266/32, bigger STM boards like the STM32F7xx, Longan Nano, BBC Micro:bit, pi pico

PWM, SPI, I2C, What’s going on?! #

For this, you’ll probably actually want a few devices to experiment with. Unless you’ve already gone out and gotten a kit of stuff, I strongly recommend against those “Lean Arduino” kits, and instead insist you buy the parts yourself. It’s a tad more expensive just to get started, but honestly a lot of the kit shit, is, well, shit.

For this section, I’ll be talking about and using a strip of WS2812b RGB LEDs (~$5), the MPU GY-521 Gyroscope/Accelerometer ($1), a HC-SR04 ultrasonic distance sensor ($5), and a ENC28J60 Ethernet breakout (~$8), as well as various parts from the Digilent Analog Parts Kit (~$55) and breadboards (don’t cheap out, get good ones ~$11) that I recommended earlier. I do realize this makes the total cost probably peak a smidge above the $100 mark. Sorry ¯\ _(ツ)_/¯

Alright, so making the board send text to the computer or blink an LED is cool and all, but, that’s not really all that interesting. Let’s go further, lets… uh… blink a different LED!

[TODO] LED pull up/down

[TODO] PWM dimming, show in slo-mo

[TODO] WS2812b, but without a library

Alright, hopefully you’re not totally bored by making lights flash, let’s have a bit more fun! We’ll start by looking at that Ultrasonic sensor

[TODO] ultrasonic sensor, basic digital input and timing, interrupts, etc.

Now, let’s look at that MPU GY-521 Gyroscope/Accelerometer

[TODO] i2c? SDA, SCL, and interrupt handling (not on all devices)

So, that’s i2c, which is nice, but sometimes you’ll want to talk to a device with full duplex communication. “Full duplex?” I hear you ask. Well, while \(\text{I}^2 \text{C}\) is a great protocol and is easy to use, it’s also only half duplex this means that, like a radio, only one side can talk at a time. Full duplex communication methods let both sides talk at once. While in a phone call this might be chaotic, there’s often times when it’s helpful to be able to send data to a device while we get data from it- for example, when working with that Ethernet adapter I said we’d be using above. For Ethernet, this is often necessary, as with TCP, a networking protocol I’ll dive into in the programming chapter, you need to repeated send replies saying “Yep, I got what you last sent, keep em’ coming” while still listening for more data.

[TODO] SPI Ethernet or SPI flash?

  • SPI has four modes, depending on CPOL and CPHA

[TODO] mention UART & CAN bus

Driving a PAL TV over RF thanks to PWM harmonics (Hackaday)

Project 1 - Calculator #

[TODO] diode matrix inputs, LED matrix outputs (7-seg), beeper, what’s a memory mapped register, bit-fields, structs, basic polling, no return from main, ever.

Project 2 - MIDI Controller #

[TODO] Gyro + photo-resistor + a few knobs & buttons, interrupts

Project 3 - ADC to the DAC #

[TODO] ADC → μC →DAC, floating point, DMA?

Depending on the board you’re using (not the Arduino Uno) you may have a/multiple Digital to Analog Converter(s) (DAC), as well as Analog to Digital Converters (ADCs) which… do what you think. ADCs let you take an analog signal in - like the output of a guitar or the voltage as you turn a knob. DACs let you output a signal (like audio). Assuming you have enough computational horsepower and the DACs and ADCs on board are good enough, the combination of these could let you do everything from a guitar pedal to making your own radio (On top of just being able to read knob positions)

Unfortunately, DACs and ADCs are complicated, and you really should know a lot about them as they’re used a lot, so, without further-a-deux:

More about DACs than you probably need to know:

  • Nyquist Rate vs Oversampling DACs
  • Decoder-based, Binary weighted, Thermometer code, & hybrid
  • Monotonictity & Linearity ; Integral & Derivative Nonlinearity (INL/DNL)

Some DACs & ADCs also allow for Direct Memory Access (DMA). A DAC with DMA, for example, will let you write samples to a buffer and at a defined rate spit them back out without CPU intervention. This allows for dramatically more complicated processing, as you no longer need to consistently get a new sample to the DAC before the next deadline (often this would need done 48,000 times a second!) Basically, DMA lets you offload some processing from the CPU. Problem? Now you need to write memory that something else is trying to access. So, how do we fix this? Well, there’s a pretty easy solution:

A ping pong buffer.

Essentially, we can just alternate back and forth between the DMA device “owning” half of the memory and the CPU owning half, alternating who gets what half every time the DMA indicates that it ran out of samples.

Note, here I’m talking about DMA as if they only apply to DACs and ADCs; however, they can be used for many other things. Commonly, this includes networking and writing to displays, though there are many other times they’re useful. So, keep this in mind.

Want to know more about ADCs and DACs? Here are some links:

Retrotechtacular: The Forgotten Vacuum Tube A/D Converters of 1965 (Hackaday)

Project 4 - LEDs… again? #

[TODO] Power with transistors, filtering, PWM. Color input from temp and magnetic field sensors

Project 5 - Euclidean Rhythms #

[TODO] Power with high, spiky loads, code with events and scheduling, watchdogs- using stepper, servo, and solenoids

Project 6 - The Epochalypse #

[TODO] low power (sleep states), graphics (eink), RTC

only 24 most significant bits, which should only change every 255 seconds, or every 4.25 minutes. Plenty of time to sit in sleep mode

[TODO] From Zero to main() (Interrupt)

Real Time Operating Systems #


[TODO note, MBED was used on the euclidean drum project for its event queue]

talk about scheduling (CFS, etc.)

Intrinsic Operations & ASM #

Your Instruction Set Architecture (ISA)

  • different operations may take a different number of instructions
  • Pipelining may make this hard to predict, especially with interrupts

Other weird and cool projects: #

Of Course It Leaks!

“The Simplest of Pseudo Random Number Generators” - Hackaday

Reverse Engineering an Unknown Microcontroller (Dmitry.GR)

Chapter 14.1 - Embedded Hardware comparison #

[TODO, Pi V RockPi4 V Arduino V x86, ref architecture information from chapter 3.3]

mention pi SD card performance / stability limits and pi advantages in how common it is

Arduino ≠ Arduino #

I really want to make this clear: Arduino is a framework. Not a specific hardware platform. Different Arduino devices can run with wildly different specs and support wildly different features, and not all devices that run the Arduino framework are made by the Arduino foundation. The Arduino Uno (ATmega328p) is both an Arduino foundation project and runs the Arduino framework. The STM32 BlackPill, NodeMCU, PiPico, etc. are all not made by the Arduino foundation, but can run the Arduino framework.

STM 32 #

The ‘Black Pill’ (STM32F411) is replacing the ‘Blue Pill’ (STM32F103) (Hackaday)

Archive.org link to Tweet

Getting Started in Robotics (Arthur Allshire’s Blog)

14.2 - Projects I recommend #

If you would like to support my development of OpGuides, please consider supporting me on GitHub Sponsors or dropping me some spare change on Venmo @vegadeftwing - every little bit helps ❤️