Skip to content

04 : Embeded Programming#

1. Summary#

Hero Shot#
Learning Outcomes#

What did I learn this week ?

  • Subject 1 :
    • 1
    • 2
  • Subject 2 :
    • 1
    • 2
  • Subject 3
    • 1
    • 2
Lecture Content#
Embedded Programming#

Embedded programming consists in controlling machines or devices by writing instructions that will then be stored and run directly on the device hence the adjective "embedded".

Microcontrollers#

Microcontrollers are small relatively cheap computers built on a single integrated circuit. They contain one or more CPUs, memory and programmable input/output peripherals and are designed for embedded applications. They are the devices which will store and run the embedded program.

In contrast to microcomputers, they do not contain any operating system which makes them highly more reactive but restrict them to relatively simple tasks.

Programming Language#

A program (i.e. the instructions) can be written in different languages by the user. It will then be translated in a language the microcontroller's processors can understand.

Programming language are often characterized by their level of abstraction. The closest it is to the machine language, the lowest level it is. It means that it will run faster since it requires a simpler translation process however it often means that it is more complicated to write and read.

Choosing your programming language must be done by considering the speed and time precision requirements of your application.

Boards#

Microcontrollers are usually fixed on a board that connects it to different devices and connection ports that makes them usable such as a USB port, a LED, a reset and a boot button, etc.

Sending instructions to the microcontroller#

Once your program is written, it must be transfered to the microcontroller's memory. The way one can transfer data to a microcontroller depends on the latter architecture.

Some complex but easy to use microcontrollers can receive data via USB flashing format (.uf2) which allows one to transfer instructions via USB connections directly from the computer on which they were written.

One the other side the simplest microcontrollers can only receive data via a single pin. Therefore one must use a device that converts USB data towards a single pin.

Bootloaders#

When a microcontroller is started, it runs the instructions that are stored in its memory. It requires instructions for everything, even for loading new instructions from another storing device. Therefore to load a program on a microcontroller, the latter must already contains instructions that tells it to check if something is available to be loaded on it. A program containing such instructions is called a bootloader.

bootloader

Some microcontrollers contains hardcoded bootloaders which are stored in a memory space which can not be touched. Therefore it is not possible to modify it hence to break it.

Some bootloaders are UF2 bootloaders meaning that it is able to directly load USB flashing format (.uf2). Such bootloaders allows the microcontroller to be detected as a USB storage by a computer when connected to it which makes the program loading process a simple "drag and drop".

Boot Mode

When a microcontroller starts normally, it does not read the bootloader instructions but instead it reads the dowloaded user program. Boards usually have a boot button that must be maintained pushed when the microcontroller is starting which tells the microcontroller to runs the bootloader instructions first.

2. Assignment#

This fourth week's asignments are :

  • Group :
    • 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
  • Individual :
    • Browse through the datasheet for a microcontroller
    • Write and test a program for an embedded system using a microcontroller to interact (with local input &/or output devices) and communicate (with remote wired or wireless connections)

Below you will find the achievement of the latter.

2.1. Individual Assignment#

2.1.1. First Steps#

Microcontroller Choice#

Since I am a beginner in embedded programming, Nicolas (former Fab Academy student and actual global instructor) helped me to choose a first microcontroller to work with. At first he suggested that I work with a RP2040 microcontroller since it contains a hardcoded UF2 bootloader that would make the loading process easy. However Michel and Fabio who wanted to do PIO were already working with the two RP2040 we had in the room. Since I would not do PIO for my first day I could start with another microcontroller and we went for the ESP32-S3.

Note

The microcontroller I got was already on a board (XIAO-ESP32-S3 by Seeed Studio) containing a USB-C port, a WiFi antenna connector and other features.

MicroPython Installation#

Since I have some experience with Python I will start with microPython language. Therefore the first thing I should do is to make the microcontroller able to understand microPython. I then have to load the microPython interpreter in the ESP32-S3 memory.

At this point we thought the ESP32-S3 contained a UF2 bootloader that would allow me to drag and drop microPython in its memory therefore I dowloaded MicroPython as a .uf2 file.

I then plugged the EPS32-S3 to my laptop with a USB cable while maintaining the boot button to start the microcontroller in boot mode. We were expecting that it would be detected as a USB storage however it did not.

We checked the ESP32-S3 Getting started by SeeedStudio and found out that it has no integrated UF2 bootloader. Therefore I must either load such a bootloader to replace the actual one or use a program that directly loads firmwares (such as microPython) from my computer to the microcontroller through its serial port.

First Option : Manually replacing the bootloader (Fail)

At first I did not know the second option so I tried the first one by following the SeeedStudio tutorial explaining how to flash .uf2 files directly to the ESP32-S3. I will summarize the tutorial here :

  • Download and extract on your local computer this zip file containing a program that replaces the microcontroller bootloader with a UF2 bootloader.

  • Connect the ESP32-S3 in boot mode to your computer and run the boot_uf2.bat script coming from the zip file.

Don't forget boot mode

At first I forgot to connect it in boot mode therefore I obtained the following error after running the boot_uf2.bat script.

C:\Users\subni\Documents\FabAcademy\embeded-prog\xiaos3-bin2uf2>fw\esptool.exe --chip esp32s3 --baud 115200  --before default_reset --after hard_reset write_flash  -z --flash_mode dio --flash_freq 80m --flash_size 8MB 0x0 fw\uf2.bootloader.bin 0x8000 fw\uf2.partitions.bin 0xe000 fw\ota_init.bin 0x410000 fw\uf2.bin
esptool.py v4.5
Found 1 serial ports
Serial port COM3
Connecting...
Chip is ESP32-S3 (revision v0.1)
Features: WiFi, BLE
Crystal is 40MHz
MAC: dc:54:75:d7:ab:c8
Uploading stub...
Running stub...
Stub running...

A fatal error occurred: Unable to verify flash chip connection (No serial data received.).

C:\Users\subni\Documents\FabAcademy\embeded-prog\xiaos3-bin2uf2>pause
Appuyez sur une touche pour continuer...

After connecting it in boot mode it worked.

C:\Users\subni\Documents\FabAcademy\embeded-prog\xiaos3-bin2uf2>fw\esptool.exe --chip esp32s3 --baud 115200  --before default_reset --after hard_reset write_flash  -z --flash_mode dio --flash_freq 80m --flash_size 8MB 0x0 fw\uf2.bootloader.bin 0x8000 fw\uf2.partitions.bin 0xe000 fw\ota_init.bin 0x410000 fw\uf2.bin
esptool.py v4.5
Found 1 serial ports
Serial port COM3
Connecting...
Chip is ESP32-S3 (revision v0.1)
Features: WiFi, BLE
Crystal is 40MHz
MAC: dc:54:75:d7:ab:c8
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Flash will be erased from 0x00000000 to 0x00005fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x0000e000 to 0x0000ffff...
Flash will be erased from 0x00410000 to 0x00432fff...
Compressed 21408 bytes to 13457...
Wrote 21408 bytes (13457 compressed) at 0x00000000 in 0.3 seconds (effective 563.6 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 136...
Wrote 3072 bytes (136 compressed) at 0x00008000 in 0.1 seconds (effective 341.6 kbit/s)...
Hash of data verified.
Compressed 8192 bytes to 31...
Wrote 8192 bytes (31 compressed) at 0x0000e000 in 0.1 seconds (effective 589.0 kbit/s)...
Hash of data verified.
Compressed 142016 bytes to 89820...
Wrote 142016 bytes (89820 compressed) at 0x00410000 in 0.5 seconds (effective 2080.8 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...
  • The new bootloader should now be loaded and the ESP32-S3 should be detected as a USB storage by your computer. You can therefore copy your .uf2 files directly in it. Once the copying is complete, the ESP32-S3 USB drive should automatically disappear, and the .uf2 program will start running.

The last step did not work for me : the ESP32-S3 USB drive was detected as a USB storage however it did not disappear after copying microPython on it. Moreover, when I tried to see if it would work anyway by sending it a microPython code with Thonny IDE, the latter did not detect the microcontroller.

Second Option : Using a flashing program

The second option is easier and uses esptool, a program that automatically flashes a new firmaware (here microPython) to the ESP32-S3. You can either use esptool in your command line interface by following this tutorial or, like me, use an IDE that allows one to do it in a graphic interface. I will use Thonny IDE and below you will find the steps I followed.

  • Launch Thonny IDE and go in Tools > Options

thonny 1

  • In the Options box, go into Interpreter, select your microcontroller and click Install or update MicroPython (esptool) (UF2)

thonny 2

  • Connect your microcontroller in boot mode

  • Select the port in which it is connected and click Install

thonny 3

  • Thonny should have installed microPython on your microcontroller. However you must restart it to exit the boot mode by clicking the reset button of your board.

  • You can now connect the Thonny terminal to your microcontroller by going into Tools>Options>Interpreter, selecting the port connected to your microcontroller and clicking OK

thonny 4

  • The Shell box should now be a terminal connected to your microcontroller. You can test it by entering a Python instruction.

thonny 5

ESP32-S3 in MicroPython#

Now that microPython is working correctly on my ESP32-S3 I will try to run some code. I first looked for a blinking led code to understand the syntax of the basic functions and I found :

led = Pin(18, Pin.OUT)
led.value(0)  # Turn the LED off
led.value(1)  # Turn the LED on
"""
This ESP32 MicroPython code was developed by newbiely.com
This ESP32 MicroPython code is made available for public use without any restriction
For comprehensive instructions and wiring diagrams, please visit:
https://newbiely.com/tutorials/esp32-micropython/esp32-micropython-blink-led
"""

from machine import Pin
from time import sleep

led = Pin(18, Pin.OUT)

while True:
    led.value(1)  # Turn the LED on
    sleep(1)      # Wait for 1 second
    led.value(0)  # Turn the LED off
    sleep(1)      # Wait for 1 second

I copied it in Thonny IDE, changed the pin value to make it correspond to the ESP32-S3 LED pin, uploaded it on the microcontroller memory and I and it worked.

In order to write original code, I wanted to rewrite it so the led would be blinking if I push button. Therefore I looked for a code reading a button value and I found :

from machine import Pin
import time

# Configure pin 5 as input with internal pull-up resistor
button = Pin(5, Pin.IN, Pin.PULL_UP)

while True:
    state = button.value()
    if state == 0:  # Button pressed (LOW due to pull-up)
        print("Button pressed")
    else:
        print("Button not pressed")
    time.sleep(0.1)  # Small pause to avoid repeated readings

I then combined the two codes, modified the pin values and wrote a blinking function to get the following code :

from machine import Pin
import time

def blink(led,freq,dur):
    N = int(freq*dur)
    T = 1/freq
    for n in range(N):
        led.value(0)
        time.sleep(T/2)
        led.value(1)
        time.sleep(T/2)

# Configure pin D10=TOUCH9 as input with internal pull-up resistor
button = Pin(9, Pin.IN, Pin.PULL_UP)

led = Pin(21, Pin.OUT)
freq = 5
dur = 2

while True:
    state = button.value()
    if state == 0:  # Button pressed (LOW due to pull-up)
        print("Button pressed")
        blink(led,freq,dur)
    else:
        print("Button not pressed")
        time.sleep(0.1)  # Small pause to avoid repeated readings

Warning

In the ESP32-S3 Hardware overview the pin D10 on which I plugged the button is refered as GPIO10 or TOUCH9. In MicroPython it corresponds to the Pin value 9 and not 10.

Here is a video of the result :

ESP32-S3 in Arduino#

I followed the Seeed Studio tutorial to install the required board and libraries. I then checked if it was working by uploading the Blink program. For that I started the microcontroller in boot mode and uploaded the Examples > Basic > Blink file.

I got the following output indicating that the uploading worked.

Sketch uses 283511 bytes (8%) of program storage space. Maximum is 3342336 bytes.
Global variables use 21572 bytes (6%) of dynamic memory, leaving 306108 bytes for local variables. Maximum is 327680 bytes.
esptool v5.1.0
Serial port COM3:
Connecting...
Connected to ESP32-S3 on COM3:
Chip type:          ESP32-S3 (QFN56) (revision v0.1)
Features:           Wi-Fi, BT 5 (LE), Dual Core + LP Core, 240MHz, Embedded PSRAM 8MB (AP_3v3)
Crystal frequency:  40MHz
USB mode:           USB-Serial/JTAG
MAC:                dc:54:75:d7:ab:c8

Uploading stub flasher...
Running stub flasher...
Stub flasher running.
Changing baud rate to 921600...
Changed.

Configuring flash size...
Flash will be erased from 0x00000000 to 0x00004fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x0000e000 to 0x0000ffff...
Flash will be erased from 0x00010000 to 0x00055fff...
Compressed 19984 bytes to 13030...

Writing at 0x00000000 [                              ]   0.0% 0/13030 bytes... 

Writing at 0x00004e10 [==============================] 100.0% 13030/13030 bytes... 
Wrote 19984 bytes (13030 compressed) at 0x00000000 in 0.4 seconds (451.7 kbit/s).
Hash of data verified.
Compressed 3072 bytes to 146...

Writing at 0x00008000 [                              ]   0.0% 0/146 bytes... 

Writing at 0x00008c00 [==============================] 100.0% 146/146 bytes... 
Wrote 3072 bytes (146 compressed) at 0x00008000 in 0.1 seconds (327.7 kbit/s).
Hash of data verified.
Compressed 8192 bytes to 47...

Writing at 0x0000e000 [                              ]   0.0% 0/47 bytes... 

Writing at 0x00010000 [==============================] 100.0% 47/47 bytes... 
Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (541.6 kbit/s).
Hash of data verified.
Compressed 283664 bytes to 153887...

Writing at 0x00010000 [                              ]   0.0% 0/153887 bytes... 

Writing at 0x0001b9b3 [==>                           ]  10.6% 16384/153887 bytes... 

Writing at 0x000281f2 [=====>                        ]  21.3% 32768/153887 bytes... 

Writing at 0x0002dba7 [========>                     ]  31.9% 49152/153887 bytes... 

Writing at 0x00033b3c [===========>                  ]  42.6% 65536/153887 bytes... 

Writing at 0x000393c0 [==============>               ]  53.2% 81920/153887 bytes... 

Writing at 0x0003ec41 [==================>           ]  63.9% 98304/153887 bytes... 

Writing at 0x000445a8 [=====================>        ]  74.5% 114688/153887 bytes... 

Writing at 0x0004d12f [========================>     ]  85.2% 131072/153887 bytes... 

Writing at 0x00052c16 [===========================>  ]  95.8% 147456/153887 bytes... 

Writing at 0x00055410 [==============================] 100.0% 153887/153887 bytes... 
Wrote 283664 bytes (153887 compressed) at 0x00010000 in 1.9 seconds (1167.9 kbit/s).
Hash of data verified.

Hard resetting via RTS pin...

I then restarted the microcontroller by pressing the reset button (to go out of the bootloader) and the LED was blinking !

I finally modified the code to get a similar program than the one I wrote in microPython (a button activating a blinking LED) :

// constants won't change. They're used here to set pin numbers:
const int buttonPin = 9;  // the number of the pushbutton pin
const float freq = 5;     // the blink frequency
const float dur = 10;     // the blink duration

// variables will change:
int buttonState = 0;  // variable for reading the pushbutton status

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
  // initialize the pushbutton pin as an pull up input:
  pinMode(buttonPin, INPUT_PULLUP);
}

// the loop function runs over and over again forever
void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed. If it is, the buttonState is LOW since it is pulled up:
  if (buttonState == LOW) {
    // start blinking
    blink(LED_BUILTIN, freq, dur);
  } else {
    // turn LED off:
    digitalWrite(LED_BUILTIN, HIGH);
  }
}

void blink(int pin,float freq,float dur) {
    int N = freq*dur;
    float T = 1/freq;
    int n;
    for (n = 0; n < N; n++) {
      digitalWrite(LED_BUILTIN, LOW);
      delay(T/2*1000);
      digitalWrite(LED_BUILTIN, HIGH);
      delay(T/2*1000);
    }      
}

and it worked perfectly.

RP2040 in MicroPython#

I followed Nicolas' tutorial to install microPython on the microcontroller using Thonny IDE (same steps than the ones I followed for the ESP32-S3).

I then copied the same code I used for the ESP32-S3, I changed the pin values after checking the XIAO RP2040 pin map and sent it to the microcontroller.

It worked ! The red LED is blinking when I press the button.

2.1.3. Button to activate a Servo motor#

Arduino Servo Library with ESP32-S3#

At first I tried to upload the Servo Example from the Arduino library (File > Examples > Examples for XIAO_ESP32S3 > Servo > Sweep) but I got an error message :

error: 'SOC_LEDC_TIMER_BIT_WIDE_NUM' was not declared in this scope; did you mean 'SOC_LEDC_TIMER_BIT_WIDTH'?

I made a research to check what this error means and I found out on the Arduino Forum that the Sevo.h library is not compatible with ESP32-S3 microcontrollers but instead one should install ESP32Servo.h library :

Installing ESP32Servo.h library on Arduino IDE

  • Launch Arduino IDE and go into Tools > Manage Libraries

thonny 5

  • The library manager box should have openned. Type "esp32servo" in the research bar and install ESP32Servo (by Kevin Harrington, John K. Benett).

thonny 5

Once the library was installed I copied the code suggested in the forum in my Arduino IDE and uploaded it on my ESP32-S3 and it was working ! The code is :

#include <ESP32Servo.h>

Servo myservo;  // create servo object to control a servo
int pos = 0;

int servoPin = 13;

void setup()
{
  // Allow allocation of all timers
  ESP32PWM::allocateTimer(0);
  ESP32PWM::allocateTimer(1);
  ESP32PWM::allocateTimer(2);
  ESP32PWM::allocateTimer(3);
  myservo.setPeriodHertz(50);    // standard 50 hz servo
  myservo.attach(servoPin, 1000, 2000); // attaches the servo on pin 18 to the servo object
}

void loop() 
{
  for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
    // in steps of 1 degree
    myservo.write(pos);    // tell servo to go to position in variable 'pos'
    delay(15);             // waits 15ms for the servo to reach the position
  }
  for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
    myservo.write(pos);    // tell servo to go to position in variable 'pos'
    delay(15);             // waits 15ms for the servo to reach the position
  }
}

However it was not working perfectly : the servo motor motion was slow and jerky. I checked the servo motor datasheet and found out that its operating voltage is ~5V while the ESP32-S3 provides a 3V tension. To make the servo motor run normally with an ESP32-S3, one should then add a supplementary power supply but since I am working from home I don't have any hence I will keep going like that today.

Button controlled Servo motor with ESP32-S3 in Arduino#

I modified the code so the Servo motor would start if I push a button :

#include <ESP32Servo.h>

const int buttonPin = 9;  // the number of the pushbutton pin
const int servoPin = 8; // the number of the servo pin

Servo myservo;  // create servo object to control a servo
int pos = 0; // variable for reading the servo position
int buttonState = 0;  // variable for reading the pushbutton status

void setup()
{
  // initialize the pushbutton pin as an pull up input:
  pinMode(buttonPin, INPUT_PULLUP);

  // Allow allocation of all timers
  ESP32PWM::allocateTimer(0);
  ESP32PWM::allocateTimer(1);
  ESP32PWM::allocateTimer(2);
  ESP32PWM::allocateTimer(3);
  myservo.setPeriodHertz(50);    // standard 50 hz servo
  myservo.attach(servoPin, 1000, 2000); // attaches the servo on pin 8 to the servo object
}

void loop() 
{
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed. If it is, the buttonState is LOW since it is pulled up:
  if (buttonState == LOW) {
    // start running servo
    servo_on();
  } else {
    // get servo back to pos=0
    myservo.write(0);
  }
}

void servo_on()
{
  for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
    // in steps of 1 degree
    myservo.write(pos);    // tell servo to go to position in variable 'pos'
    delay(15);             // waits 15ms for the servo to reach the position
  }
  for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
    myservo.write(pos);    // tell servo to go to position in variable 'pos'
    delay(15);             // waits 15ms for the servo to reach the position
  }
}

Here is a video showing the result :

2.1.4. OLED screen#

Installation#

A last small project I did this week is to control an OLED screen with a SAMD21 microcontroller (XIAO-SAMD21 board). Patrick showed to me how to solder the XIAO board and the OLED screen with a copper PCB (Check Week ?? documentation for more informations) which I then did.

The screen is a SSD1306 OLED display and I will use Arduino IDE and language to control it. Therefore I looked for Arduino tutorials and I followed this one to install the required libraries.

I also installed the XIAO-SAMD21 board on my Arduino IDE.

Text display#

I copied the "Hello World !" code from the tutorial and modified it to display "IT WORKS!" with a larger font and a centered position :

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() {
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(20, 15);
  display.print("IT WORKS!");
  display.display();
}

void loop() {
}

and it worked :

Video

2.2. Group Assignment#

To do : Document our discussion