Skip to content

Week 04 - Embedded Programming

Here are the assignments of week 04.

Group Assignment:

  • Demonstrate and compare the toolchains and development workflows for available embedded architectures
  • Document your work to the group work page and reflect on your individual page what you learned

Please refer to the group page to read about our group assignment, where I was responsible for the ESP32-S3.

Individual Assignment:

  • Browse through the datasheet for your microcontroller
  • Write a program for a microcontroller, and simulate its operation, to interact (with local input &/or output devices) and communicate (with remote wired or wireless connection)

Learnings from the group assignment

  • I got a glimpse of the variety of microcontrollers and their different properties,but also what to look for, when selecting a microcontroller for a project.

  • Some microcontrollers are available as modules - meaning on a PCB with USB socket and/or other peripherals, allowing for quick and easy prototyping.

  • The toolchains and workflows vary between the microcontrollers and there are usually several different ways of how to connect, what language to use and how get the program to the memory of the chip. Each approach might have it's own advantages and disadvantages.

  • Debugging capeabilities are important and a good IDE/compiler will provide you with useful information on possible issues. This would be much harder, when using assembly language.

  • For SoC microcontrollers, there is additional hardware required for being able to program it, while modern modules or development boards just require an USB cable.

Selecting a microcontroller

For this week's assignment we are supposed to select a microcontroller and simulate its operation.

First I needed to get a better understanding of all this. So I remembered from the lecture, that ESP32 is powerful and capable of Bluetooth and Wifi communication and therefor I decided to give it a try.

Soon I realized, that there are many types of ESP32 and I started browsing the homepage of the manufacturer.

First I went to the product selector to figure out what the differences are and how I could possibly select one type.

Product Selector

There is a selection of 256 types available (excluding development boards).

Next I checked the categories and which of them I can identify. Obvisously, there are different generations of Wifi and Bluetooth that are used in the different microcontrollers. Then they have a different temperature resistance and package types.

The memory has different sizes and the peripherals differ - meaning the possible ways of interacting with the microcontrollor. Also I can choose between SoC and Module. I found out, that SoC stands for System on a chip and it is only a bare chip, while the module contains the SoC on a PCB, together with other things like an antenna and is more "ready-to-use" than the SoC. So if I would really like to use the wireless connection, I should probably pick the module. For the simulation however, it shouldn't make a difference.

After narrowing the search more by selecting a type of Wifi and Bluetooth, Mass produced compenents and so on, I still got 33 modules to choose from. Then I saw the link to this comparison sheet.

ESP32 comparison

With the help of that file, I decided to use the ESP32-S3 as it is available as a module, supports BLE and WIFI and has 45 GPIO Pins, so it seems to be a powerful and versatile product.

The product selector then showed me 25 different modules with the ESP32-S3. I don't feel like I could confidently selec one, but also I think it doesn't really matter at this point. This assignemnt is more about the microcontroller itself and I have decided on it.

Datasheet

Here is a link to the datasheet of the ESP32-S3 Series.

The first thing I noticed is, that the datasheet is "only" 84 pages. I expected more, because we talked about between 500 and 1000 pages datasheets for other types, if I remember correctly, but I won't complain.

Below, I'm going to mention the main findings from the datasheet and highlight some possibly important properties of the mirocontroller, that I identified.

Here is a block diagram for the SoC. It is devided in several categories as the CPU and memory, RF (radio), Wireless digital circuit for Wifi and Bluetooth, Security?, RTC and Peripherals.

ESP32-S3 block diagram

Note

The processor has a word size of 32 bits and a dual-core. It runs at up to 240 Mhz clock speed.

Some of the components can run in a deep-sleep mode using less power. They are highlighted in the diagram.

Note

RTC stands for real time clock, which is a component keeping track of the time passing in a very accurate way.

The processor uses a Single precision floating point unit (FPU). There are two expressions here that need explaning.

  1. The Floating point unit (FPU) is also called math coprocessor and is part of any modern computer system. It is a specialized hardware component that can efficiently perform floating-point number calculations. 60sec.site

  2. Single precision (floating-point format) is also called FP32 or float32 and is a number format using 32 bit of memory. According to the Wikipedia article it can reach a maximum value of \(3,4028235\times10^{38}\). It is built up in a clever way, using three different sections to define different "properties" and achieves an accuracy of seven to eight decimal digits. Bit 1: Positive or negative Bit 2 - 9: Exponent Bit 10-32: Fraction

For the peripherals the microcontroller seems to have a lot of GPIOs and depending on the variant of the chip, there are six or seven of them preoccupied.

There are peripherals for usb serial, motor control, LCD, LED PWM controller and much more.

There are also analog interfaces including two 12-bit SAR ADCs.

SAR ADC stands for successive-approximation analog-digital converter. It's a circuit type used to measure analog input voltage and convert it to a digital value. As the name implicates, the accuracy of the reading increases for each iteration and it uses different binary weights assigned to each bit, depending on the number of bits and the voltage range. Wikipedia

There are four power modes available: active, modem-sleep, light-sleep and deep-sleep.

Note

In deep sleep the current draw can be as low as 7 µA.

The Microcontroller features two Ultra-Low-Power (ULP) coprocessors, which can be used to run operations, while the CPU is in sleep mode, nut not simultaneously. One coprocessor uses RISC-V instruction set architecture and the other a finite-state machine (FSM). We already learned about RISC in the group assignemnt, meaning HARVARD architecture, but what is FSM?

Finite-state machine is a model of computation where the abstract machine can be on one of a finite number of states, depending on inputs. Wikipedia

Here is the pin layout of the ESP32-S3

Pin layout

Note

The drive strength between the pins is different. GPIO17 and 18: 10 mA GPIO19 and 20: 40 mA all other: 20 mA

It is written, that the IO MUX Function allows multiple input/output signals to be connected to a single pin. I don't understand how that is possible, but I'll just accept it for now.

The chapter about the power supply states the pins needed for powering the chip. I'm suprised to learn, that there are seven voltage input pins but only one ground pin.

The chapter about boot configurations talks about burning eFuses to achieve a certain boot mode and that it is not revertable, once done... I think more important for beginners is the chip boot mode control via GPIO0 and GPIO46. As I understand it, they control of you can upload a via UART or USB.

Note

The ESP32-S3 includes a random number generator, that generates true random numbers from a physical process - not with an algorithm. Interesting!

The UART controller supports also RS232 and RS485 commumication, which are both protocols I use very often - so that is good.

The ESP32-S3 has four independent pulse counters. This is maybe something I would use for counting rotation of a fast spinning component like a propeller?

The ESP32-S3 has an integrated temperature sensor to observe the internal temperature of the chip.

It also features 14 capacitive sensing GPIOs for touch sensors. I wasn't aware that these require special GPIOs, but we haven't learned about input devices yet. It might be interesting trying that one day.

In the chapter about electrical characteristics is a table for maximum voltage and current, which I'm going to post here, because it might be of importance.

maximum voltage and current

Info

The recommended power supply is typically between 3,0 and 3,6 V.

The last chapter describes the packaging (footprint) of the chip and how it is provided.

The technical drawing shows the chip having outer dimensions of 7 x 7 x 0,85 mm.

Dimensions

For more detailed information, there is a

available, which provides in-depth information on more then 1500 pages.

There are also

for those who want to integrate the ESP32-S3 in an own design.

More interesting content is linked in the datasheet:

Concluding, I found this datasheet quite interesting, even though I understood less then 5% of its content, I think I got a better feeling of the structure and components inside the microcontroller and could search more confidently, if I would need to look up any specific features.

I also took a quick look at the Technical Reference Manual which goes very much into detail of all the bits and operations, so I found it quite overwhelming.

The Hardware Design Guideline seems to be relatively straight forward and I assume it provides good instructions on how to design a PCB with the ESP32-S3, even though this is of course much more complicated than I can imagine at this point.

Simulating a program

The simulation will take place in the online environment of Wokwi.

First I logged in, because I want to save my project.

Then I selected the ESP32 and the starter template for the ESP32-S3, which is actually the ESP32-S3-WROOM-1 module.

Wokwi

As you can see, the RGB LED is on the PCB and the antenna as well.

In the library manager I can select and load all the libraries I may need.

The example is written in C++, but you can also select Micropython. As I usually use C++, I thought it might be a good idea to simulate a Micropython program.

Example program

I searched for a project to use as a starting point and found this very nice example, using a small OLED display SSD1306 and an accelerometer + gyroscope MPU6050.

Wokwi example

Here is the original code.

esp32-micropython-ssd1306-mpu6050
from machine import Pin, I2C
import ssd1306
import mpu6050
import time


# ESP32 Pin assignment 
i2c = I2C(0, scl=Pin(22), sda=Pin(21))

oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)

mpu = mpu6050.MPU6050(i2c)

# wake up the MPU6050 from sleep
mpu.wake()
gyro = ""
accel = ""
temp = ""
# continuously print the data
while True:
    new_gyro = mpu.read_gyro_data()
    new_accel = mpu.read_accel_data()
    temperature = mpu.read_temperature()
    if(new_gyro != gyro or new_accel != accel or temperature != temp):
      gyro = new_gyro
      accel = new_accel
      temp = temperature
      print("Gyro: " + str(gyro) + ", Accel: " + str(accel))
      oled.fill(0)
      oled.text("Gyro", 0, 2)
      oled.text("-----------------", 0, 10)
      oled.text("x:"+ str(round(gyro[0],2)), 0, 15)
      oled.text("y:"+ str(round(gyro[1],2)), 0, 25)
      oled.text("z:"+ str(round(gyro[2],2)), 0, 35)
      oled.text("Accel", 70, 2)
      oled.text("x:"+ str(round(accel[0],2)), 70, 15)
      oled.text("y:"+ str(round(accel[1],2)), 70, 25)
      oled.text("z:"+ str(round(accel[2],2)), 70, 35)
      oled.text("Temp", 0, 45)
      oled.text("------------"+str(temperature), 0, 48)

      oled.show()
    time.sleep(0.1)

Adapting the example

To make something that better suits my needs, I'm going to try to measure the tilt an roll angle of the MPU6050 and convert it to a wind direction and speed for a first iteration. That is something that might be useful for my final project.

First I made a new project in Wokwi, saved it and added the ESP32-S3, MPU6050 and SSD1306. Then I copied the important code lines from the example and removed the gyro part. as I'm only interested in the acceleration for now.

I found this site, which provides the code for the calculation of of the angle.

Now let's assume that the X-positive is pointing towards the south and Z-positive down to earth center. A deflection by a northerly wind would cause an acceleration in +X. That results in a positive pitch angle. Vice versa does an easterly wind cause a deflection in +Y resulting in a positive roll angle.

Basically, I need to convert the cartesian coordinates of the pitch and roll angles into polar coordinates. The vector angle is the wind direction and the length is proportional to the speed.

Here is the next iteration running, where I plot the wind direction and speed in the terminal.

When moving the acceleration bars, the wind direction and speed values printed change.

Wind vector

from machine import Pin, I2C
import ssd1306
import mpu6050
import time
import math

# ESP32 Pin assignment 
i2c = I2C(0, scl=Pin(40), sda=Pin(41))

oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)

mpu = mpu6050.MPU6050(i2c)

# wake up the MPU6050 from sleep
mpu.wake()
gyro = ""
accel = ""
temp = ""
# continuously print the data
while True:
    new_accel = mpu.read_accel_data()
    if(new_accel != accel):
      accel = new_accel
      pitch = math.atan(accel[0]/accel[2])
      roll = math.atan(accel[1]/accel[2])
      if roll > 0:
        wdir = math.atan2(roll,pitch)*180/math.pi
      else:
        wdir = math.atan2(-roll,-pitch)*180/math.pi+180

      wspd = math.sqrt(pow(pitch,2) + pow(roll,2))

      print("Wdir: " + str(int(wdir)) + " Wspd: " + str(wspd))


    time.sleep(0.1)

For the next iteration, I would like to show the values on the screen, but instead of degrees, I would like to use abbreviations for the wind direction.

This can be done using an ifelse function and write the abbreviations in a string.

Here is a video, that shows me manipulating the input values for the acceleration and display the wind direction and speed on the screen.

And here is the code for the second iteration.

from machine import Pin, I2C
import ssd1306
import mpu6050
import time
import math

# ESP32 Pin assignment 
i2c = I2C(0, scl=Pin(40), sda=Pin(41))

oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)

mpu = mpu6050.MPU6050(i2c)

# wake up the MPU6050 from sleep
mpu.wake()
gyro = ""
accel = ""
temp = ""
pitch = ""
roll = ""
wdir = ""
wspd = ""
wdstr = "O"
# continuously print the data
while True:
    new_accel = mpu.read_accel_data()
    if(new_accel != accel):
      accel = new_accel
      pitch = math.atan(accel[0]/accel[2])
      roll = math.atan(accel[1]/accel[2])
      if roll > 0:
        wdir = math.atan2(roll,pitch)*180/math.pi
      else:
        wdir = math.atan2(-roll,-pitch)*180/math.pi+180

      wspd = math.sqrt(pow(pitch,2) + pow(roll,2))

      if(wdir < 11.25):
        wdstr = "N"
      elif(wdir < 33.75):
        wdstr = "NNA"
      elif(wdir < 56.25):
        wdstr = "NA"
      elif(wdir < 78.75):
        wdstr = "ANA"
      elif(wdir < 101.25):
        wdstr = "A"
      elif(wdir < 123.75):
        wdstr = "ASA"
      elif(wdir < 146.25):
        wdstr = "SA"
      elif(wdir < 168.75):
        wdstr = "SSA"
      elif(wdir < 191.25):
        wdstr = "S"
      elif(wdir < 213.75):
        wdstr = "SSV"
      elif(wdir < 236.25):
        wdstr = "SV"
      elif(wdir < 258.75):
        wdstr = "VSV"
      elif(wdir < 281.25):
        wdstr = "V"
      elif(wdir < 303.75):
        wdstr = "VNV"
      elif(wdir < 326.25):
        wdstr = "NV"
      elif(wdir < 348.75):
        wdstr = "NNV"
      else:
        wdstr = "N"

      print("Wdir: " + str(int(wdir)) + " Wspd: " + str(wspd))

      oled.fill(0)
      oled.text("Wind speed: " + str(wspd), 0, 15)
      oled.text("Wind dir:   " + wdstr    , 0, 35)      

      oled.show()
    time.sleep(0.1)

For the third and last iteration it would be nice to have the module connect to a Wifi network and send the wind data to an MQTT broker.

If found this Wokwi project that does exact this and copy - pasted the important code parts into my project. I'm going to use the HiveMq online broker to receive the messages for this test.

Wokwi has a simulated Wifi that you can connect to. In the loop I create a one-line message containing wind direction and speed.

I've already defined the topic "wind" for the message, so I need to connect to the Websocket client and listen to that topic.

Here is a video, showing me changing the inputs and receiving messages in the websocket.

And here is the code for the third and final iteration.

from machine import Pin, I2C
import ssd1306
import mpu6050
import time
import math
import network
from umqtt.simple import MQTTClient

# MQTT Server Parameters
MQTT_CLIENT_ID = "anemo_test"
MQTT_BROKER    = "broker.hivemq.com"
MQTT_USER      = ""
MQTT_PASSWORD  = ""
MQTT_TOPIC     = "wind"

# WIFI Connection
print("Connecting to WiFi", end="")
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.connect('Wokwi-GUEST', '')
while not sta_if.isconnected():
  print(".", end="")
  time.sleep(0.1)
print(" Connected!")

# MQTT Server connection
print("Connecting to MQTT server... ", end="")
client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER, user=MQTT_USER, password=MQTT_PASSWORD)
client.connect()
print("Connected!")

# ESP32 Pin assignment 
i2c = I2C(0, scl=Pin(40), sda=Pin(41))

oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)

mpu = mpu6050.MPU6050(i2c)

# wake up the MPU6050 from sleep
mpu.wake()
gyro = ""
accel = ""
temp = ""
pitch = ""
roll = ""
wdir = ""
wspd = ""
wdstr = "O"
# continuously print the data
while True:
    new_accel = mpu.read_accel_data()
    if(new_accel != accel):
      accel = new_accel
      pitch = math.atan(accel[0]/accel[2])
      roll = math.atan(accel[1]/accel[2])
      if roll > 0:
        wdir = math.atan2(roll,pitch)*180/math.pi
      else:
        wdir = math.atan2(-roll,-pitch)*180/math.pi+180

      wspd = math.sqrt(pow(pitch,2) + pow(roll,2))

      if(wdir < 11.25):
        wdstr = "N"
      elif(wdir < 33.75):
        wdstr = "NNA"
      elif(wdir < 56.25):
        wdstr = "NA"
      elif(wdir < 78.75):
        wdstr = "ANA"
      elif(wdir < 101.25):
        wdstr = "A"
      elif(wdir < 123.75):
        wdstr = "ASA"
      elif(wdir < 146.25):
        wdstr = "SA"
      elif(wdir < 168.75):
        wdstr = "SSA"
      elif(wdir < 191.25):
        wdstr = "S"
      elif(wdir < 213.75):
        wdstr = "SSV"
      elif(wdir < 236.25):
        wdstr = "SV"
      elif(wdir < 258.75):
        wdstr = "VSV"
      elif(wdir < 281.25):
        wdstr = "V"
      elif(wdir < 303.75):
        wdstr = "VNV"
      elif(wdir < 326.25):
        wdstr = "NV"
      elif(wdir < 348.75):
        wdstr = "NNV"
      else:
        wdstr = "N"

      #print("Wdir: " + str(int(wdir)) + " Wspd: " + str(wspd))

      oled.fill(0)
      oled.text("Wind speed: " + str(wspd), 0, 15)
      oled.text("Wind dir:   " + wdstr    , 0, 35)       
      oled.show()

      message = "Wind: " + wdstr + " " + str(round(wspd,2))
      print(message)

      client.publish(MQTT_TOPIC, message)

    time.sleep(1)

The Wokwi project is linked here.