Tag Archives: embedded

Guide to Modular Firmware

This article is for embedded software developers with good knowledge of C or C++, but struggle with large and complex projects.

If you learn developing embedded code, e.g. using the Arduino IDE, you find plenty of small example programs. It is helpful for a quick start, but as soon your project grows, help about software design is rare.

In contrast, if you learn software development for desktop applications, project structures and software design is an integral part of the learning process.

With this short article, I will give you a simple guideline, how you can build a modular structure for your firmware, which will keep your code clean and maintainable for large and complex projects.

Refactor your Code

If you start a new project, you can already prepare the structures as described. I assume in this article you already have a working firmware but need to improve the code quality.

Improving the code in an iterative process is called refactoring. Testing is an integral part of this process. After each small change, you test if the software is still working as expected.

In desktop application development, there are unit tests to ensure the integrity of smaller modules. I found it difficult to apply unit tests to embedded code, if not for small independent functions or modules. Therefore you have to use a simple run-time test of your software, to make sure it is still working as expected.

Refactoring only changes the code, but not the functionality. Even if you change names, move code around and change implementations, the function of your code stays exactly the same. It is important you either change or extend functionality or do refactoring, but never do both at the same time (or in the same commit).

Use a Version Control System

Changing your code without version history is a bad idea. If you do not already manage your code in a version control system, now it is the time to start using one.

If you never used a version control system before, use GIT and read one of the many tutorials on how to use it. There are graphical user interfaces for any operating system, so you do not have to work on the console. It does not matter how you manage your code – it is important that you use a version control system.

After each small successful change, you should commit a new version. If you run into troubles at a later stage, you can easily analyse every change you did on the code and go back to the last working version.

The Demo Setup

If you like to follow along using the real demo setup, you will need an Arduino Uno, three LEDs with matching resistors and two pushbuttons. The example code expects a circuit shown in the next illustration.

A Shockingly Bad Example to Start With

The example code to start with is something I sadly see often. Please open a second browser window with the code at the following URL:

https://github.com/LuckyResistor/guide-modular-firmware/blob/master/fade_demo_01/fade_demo_01.ino

I can not use a really complex firmware for this article, and your source code may be in a different state. Nevertheless, this example code contains most of the elements I like to discuss.

Because of the length of the code, I will just link to the full examples. The code snippets in the article should have the correct line numbers, so you can easily find the locations.

Continue reading Guide to Modular Firmware

Event-based Firmware (Part 2/2)

In the first part of this article, we explored the general concept of event-based firmware. For the first part, please click this link.

The introduced concepts were directly tailored to one specific firmware. Now let us develop the concepts further to build an event system which can be integrated in many different applications.

Let us analyze the last exampe:

blink8.ino

#include "BlinkLed.hpp"
#include "Event.hpp"

Event gBlinkLedEvent;

void setup() {
  BlinkLed::initialize();
  gBlinkLedEvent.start(10);
}

void loop() {
  const auto currentTime = millis();
  if (gBlinkLedEvent.isReady(currentTime)) {
    const auto scheduledDelay = BlinkLed::event();
    gBlinkLedEvent.scheduleNext(scheduledDelay);
  }
  while (millis() == currentTime) {}
}

What makes this code static?

First, the variable gBlinkLedEvent is directly tied to this single event. It can not be reused by other events.

Second, the if (gBlinkLedEvent.isReady(currentTime)) { ... } code is repetitive for all events. If you have repetitive code, it is usually a sign of bad design. While there are very few cases where repetitive code is acceptable, usually it should be removed using structures, loops and tables.

Backtrack for a New Direction

Let us address the issue with the fixed function call first. To solve this, we have to backtrack and enter a new direction. The next example consists of the files blink9.ino, Event.hpp and Event.cpp:

Continue reading Event-based Firmware (Part 2/2)

Event-based Firmware (Part 1/2)

In this article, I explain the event-based approach for writing an embedded firmware. While I use the term event-based, it is similar to events systems used for desktop application but much more straightforward. It is nothing new or innovative; the shown approach is just good practice to keep your firmware modular and extensible.

I will guide you in small iterations, using practical examples, through this complex topic. You can stop at any point and start improving your code to that level of complexity you fully understand and can handle.

What are Event-Driven Applications?

For desktop applications, an event-driven approach is chosen because the software always reacts to events generated by the user. Each movement of the mouse, key and mouse button press and release will generate an event. These events are pushed into a queue and processed in an event loop.

The second important source of events is timers. Either fixed timers, which will e.g. generate an event every 100ms – or delayed timers which will create an event after a defined delay.

For embedded code, we mostly use timers as an event source. If implemented correctly, you can easily generate events from button presses, interrupts or other asynchronous sources as well.

In this article, I will focus on events generated by timers. These will solve most of the problems in firmware. Asynchronous input sources can be implemented using polling to integrate them into the event-based approach.

The Blink Example – Without Events

Let us start with a simple example application, you can find for any platform. For this article, I will explain everything based on examples for the Arduino Uno platform, using the Arduino IDE. I do this because anyone can easily reproduce the examples, and it will remove the additional complexity of the actual hardware access layer from the code.

Nevertheless, the shown concepts are meant to be used on any platform, especially for real-world applications. I try to explain the core concepts to you, so you will be able to apply them in any situation.

The Simple Blink Example

blink1.ino

const uint8_t cOrangeLedPin = 13;

void setup() {
  pinMode(cOrangeLedPin, OUTPUT);
}

void loop() {
  digitalWrite(cOrangeLedPin, HIGH);
  delay(1000);
  digitalWrite(cOrangeLedPin, LOW);
  delay(1000);
}

The Example Explained

If you are not familiar with the Arduino libraries: There are two predefined functions, setup() and loop(). The setup() function is called once after the start to set up things and the loop() function is called endlessly afterwards. This structure is already prepared for the event-based approach.

In the blink example above, I set the pin where a red or orange LED is attached as output, using pinMode() and in the loop, I toggle the output from low to high and back with a delay of one second.

While this example works perfectly, it is not very extensible or modular. If you like to e.g. blink a second LED on another pin, test the states of several sensors and buttons, you will get into trouble.

Introducing a Real-Time Counter to use Time Points

Instead of just blocking the CPU until an amount of time has passed (using the delay function), a better approach is to introduce a real-time counter. Often this is also called real-time clock, which can easily get confused with the special chips which hold the current date and time. This is just a counter which starts at zero at the start of the firmware and is increased every millisecond or another time unit.

If you write a firmware from scratch, you use a hardware timer and interrupts to create a timer like this:

// pseudocode:

uint32_t gTimer = 0;

setup {
    // setup the hardware timer to create an interrupt
    // every millisecond.
}

interrupt {
    ++gTimer;
}

In the Arduino environment, this is already implemented, and there is the function millis() to get the current value of the counter. The counter will flip over at the highest value and restart at zero.

Continue reading Event-based Firmware (Part 1/2)