top of page
  • Writer's pictureConnor Humiston

My 1st Embedded System

Updated: Sep 22, 2020

This project showcases the creation of a multipart embedded system that reads temperature measurements, sends them via Bluetooth to a connected phone every 3.1 seconds, and switches between Celsius and Fahrenheit on command. Furthermore, an LED illuminates if the temperature exceeds 80 degrees F. Temperature readings are taken from the Si7021 sensor on the EFM 32 Pearl Gecko microcontroller board that the embedded system runs on. Drivers for the low energy timer that controls the timing between temperature readings, the scheduler that maintains the lowest possible energy mode throughout all applications, the interrupt driven I2C state machine that interfaces with the Si7021 sensor, the low energy UART that transmits to and receives from the bluetooth module, and the circular buffer that holds data going to peripherals while other applications are producing were coded in C in Simplicity Studio to govern the project. To skip to the code, click here.

EFM32 Pearl Gecko Microcontroller Board

The embedded system was built in a series of steps throughout the semester. First, I familiarized myself with the Simplicity Studio IDE and EMF32 Pearl Gecko Microcontroller Board.

Next, the low energy timer was configured. The LETIMER is a down-counter that can keep track of time and output configurable waveforms. At first, it was used to pulse the LED at 3.1 second intervals. Later in the project, the LETIMER was also used to take regular temperature measurements.


One of the primary objectives throughout the project was to maintain the lowest energy operation possible. The Pearl Gecko was useful in achieving that goal for many reasons. First, the Pearl Gecko offered five energy modes of operation (EM0 - EM4) that provide flexibility between higher performance (EM0 side) and lower power (EM4 side). For example, the LETIMER can operate in EM0 Active, EM1 Sleep, EM2 Deep Sleep, and EM3 Stop, but not EM4 Hibernate. The I2C bus and LEUART, on the other hand, require EM0 operation. The microcontroller also offers a variety of clock sources from a 1000Hz ultra-low frequency RC oscillator (ULFRCO) for low energy to a 38MHz high frequency crystal oscillator (HFXO) for high precision. Each oscillator can be multiplexed on or off individually, pre-scaled, and AND gated at each peripheral. This versatility ensures that only the oscillator you want and the peripheral you want is using energy.


Clock Tree exhibiting the CMU's high frequency oscillators, pre-scalers, and gates

Clock Tree illustrating the lower frequency oscillators, pre-scalers, and gates

Pre-scalers were not used in the project, but the oscillator and clock gates had to be configured for each additional application throughout the project. Routing locations for each peripheral were also found and connected. In the case of the LETIMER, after initializing the system, the PWM was used to pulse the LED without intervention from the most energy consuming peripheral, the CPU.

Simplicity Studio made monitoring energy use easy with its Energy Profiler. The image below shows current spikes in the Energy Profiler every 3.1 seconds when the LETIMER tells the LED to illuminate.

Energy Profiler showing current spikes every 3.1 seconds

As interrupt-driven components were added to the project, implementing a scheduler made a lot of sense. The scheduler utilized the concept of a state machine and made the code easier to develop, test, and debug. Scheduler events were set via Interrupt Service Routines and cycled through each time and interrupt occurred until the event is serviced.


Speaking of debugging, EFM_ASSERT statements were used throughout to alert for project failures before crashing. Doxygen commenting was also used to make for html/website-readable code. When run through the Doxygen wizard, a website showing each .h and .c file as well as the functions and arguments within are created.


Si7021 Temperature Sensor (with cover)

The next step in the project was implementing a modular, encapsulated, interrupt-driven Inter-Integrated Circuit (I2C) driver to read the temperature from the Si7021 sensor located in an isolated part of the Pearl Gecko board. The goal of this addition was to request temperature readings every 3.1 seconds upon an LETIMER interrupt via the I2C bus. To achieve this I2C read, a software state machine was developed to transition from one state to the next state upon interrupts. Between these interrupts, the Pearl Gecko should remain in the lowest possible energy state. When the temperature surpasses 80 F, LED1 will turn-on indicating a high temperature and will turn-off when the temperature returns below 80 F.


To explain a little further, I2C is one of the most prevalent digital communication buses for embedded systems where sensors communicate with a microcontroller. The bus protocol supports multiple devices on the same bus through a device addressing scheme as well as supporting multi-bus masters. The I2C serial bus requires only 2-signals/GPIO pins (SDA and SCL) by the microcontroller enabling its use with small and inexpensive microcontrollers.


Furthermore, interrupts were chosen for the project for their low energy nature (especially when compared to polling) and for their multi-tasking nature. This became important when both reading the temperature from the I2C sensor and writing data to a device via bluetooth concurrently. When the communication bus is waiting for a response, the microcontroller can move to another task and then return to the first task when the appropriate interrupt is received. Developing an Interrupt Driven State Machine was critical to enable multi-tasking of embedded system


To visualize how the driver works, a software ladder chart was made. It illustrates the state machine, sensor interrupts, and software flow.

Software Flowchart for the Si7021 Temperature Sensor that communicates via I2C

After routing the I2C's SDA and SCL ports and pins and configuring the oscillators and clocks, the I2C functions were written. The Energy Profiler shows the application in progress.

Energy Profiler after adding temperature reading

The current spikes in the image above represent temperature readings and I2C communication, the step-up shows the LED turning on when 80F was reached (by holding my finger on the sensor), and the step-down shows the LED turning off after temperature dips below 80F.


After zooming in on the current spikes, the transmission of individual characters on the I2C bus become apparent.

Energy Profiler zoomed in on current spikes showing I2C character transmissions

With the Si7021, I2C bus, scheduler, oscillators, and clocks working properly, Bluetooth connectivity is next. The HM-19 bluetooth module was used.

HM-19 Bluetooth 5.0 Low Energy Module

To connect the Bluetooth Low Energy (BLE) module to the Pearl Gecko, the Pearl Gecko's TX UART expansion connection was wired to the BLE RXD pin and the Pearl Gecko's RX UART expansion was wired to the BLE TXD pin with female to female pin connectors.

Pearl Gecko Expansion Connectors

To send data with the bluetooth module, the low energy UART (LEUART) was used. The LEUART connects with the BLE Network Co-Processor (NCP), a very simple way of communicating via a radio. The NCP handles the software communication stack and the application on the Pearl Gecko just transmits the data to the NCP for transmission or receive of data from it.


The UART interface is one of the three most common digital communication buses to connect sensors and small actuators to a microcontroller. With the addition of an RS-232 driver which changes the voltage range from 0-3.3v to -12 to +12v, the UART via RS-232 is an effective communication protocol out of an embedded system using cabling. The UART peripheral is a simpler communication protocol than I2C. UART interfaces are normally point to point data links, so no protocol is required to address which device that the Pearl Gecko would like to communicate when it requests a read or write on the UART communication bus. The UART protocol also does not require the slave or end point to acknowledge receipt of data on a write.


The UART drivers were implemented using the Test-Driven Development (TDD) methodology, a developing a process that indicates errors in project code with unit tests that focus on small segments of code to reduce the debug effort later. The TDD methodology provides two additional key benefits to the developer and overall project. First, the Test-Driven Code is designed to validate system requirements and not a specific implementation. By testing the system requirements, once the code passes the TDD test, the code developed will most likely pass the project’s overall system verification and validation. Secondly, the TDD code should be designed to remain in the system while code is being developed. These unit test become regression tests that will indicate an error if another piece of code being developed results in a failure of a previously completed code segment.


The following state machine was designed before the TDD method to ensure all parts of the driver were accounted for.

LEUART Software Flowchart and State Machine

After finishing the LEUART and BLE drivers, bluetooth character transmissions started showing up on the Energy Profiler.

Energy Profiler Screenshot after BLE/LEUART driver added

With the current setup, one issue becomes apparent. The Bluetooth system works well for sending a simple temperature reading every 3.1 second interval. But what if multiple strings needed sent. Being a data driven device, the data from the source or "producer" (the temperature sensor) is used to make decisions by the "consumer" (the driver). The issue is that the rate that data can be produced and consumed are not necessarily at the same rate (if the sensor is producing more data to be sent than the Bluetooth driver is able to send out). The solution: a circular buffer.


A circular buffer is a First in First Out (FIFO) memory that loops around itself. The concept of this buffer is to take data from the producer and store it until the consumer is ready to accept and send the data. This buffer had to be sized and designed correctly to minimize the chance that the circular buffer would fill or overflow before the consumer could retrieve the data.

Like most, my circular buffer uses push and pop methods to push data onto the circular buffer storage and pop data off when it's ready to be sent. These flowcharts help to visualize the methods.

Flowchart depicting the Circular Buffer's push method

Flowchart depicting the Circular Buffer's pop method

After completing the circular buffer functions (with its TDD) and sending a couple additional strings with a few more lines to in the application code, it was time to connect a Bluetooth receiver device to the system. I used the BLE Terminal HM-10 app on my iPhone to connect to the device.

After connecting the Bluetooth module to my phone, the Energy Profiler reflected my additions.

Energy Profiler after completing Circular Buffer

As expected, the system is going to sleep between each character transmission for each string sent. The output on my terminal looked like this.

Terminal Output with addition of Circular Buffer

You'll notice that the current spikes match up to the characters being sent. The temperature readings continued to occur every 3.1 seconds after the welcome strings.



Ultimately, the purpose of the embedded system's Bluetooth module is to both send and receive data. At this point, the system can send the temperature and any number of strings successfully, but receiving would be useful to accept commands from the bluetooth terminal on my phone.


I expanded on this idea by updating my LEUART module to receive commands that change the displayed temperature output from degrees F to degrees C and vice versa. Following low energy design principles, the LEUART module remained unable to receive characters until a START Frame was acknowledged. The system also waits to decode the message until a SIGNAL Frame is accepted, at which point the STARTF is re-enabled to allow follow-up commands. The following software flowcharts illustrate this concept.

Flowchart exhibiting the flow of events for a Bluetooth write
Flowchart exhibiting the flow of events for reading in a Bluetooth command

The following screenshot reveals the final output after all the drivers were finished. The first three lines are TDD tests. The next three show the welcome messages. Then, temperature readings are sent. You'll note the #C? command to change from F to C. The # is the START Frame and the ? is the SIGNAL Frame. Then, the #F? was send to change back to Fahrenheit.

Bluetooth Terminal showing project functionality

To see the code that drove this project, here's the GitHub link. To recap, in this project I explored and wrote C code for the following modules:

  • cmu (Clock Management Unit)

  • gpio (General-Purpose Input/Output)

  • letimer (Low Energy Timer)

  • scheduler (manages application queue)

  • sleep_routines (Low Energy Sleep Management)

  • i2c (Inter-Integrated Circuit bus)

  • Si7021 (temperature sensor)

  • ble (Bluetooth Low Energy)

  • leuart (Low Energy UART receiver/transmitter)


Also, here's how the Doxygen website turned out if you're interested.


0 comments

Recent Posts

See All
bottom of page