Skip to content

8. Embedded programming

Group assignment

As one will see in my personal assignment I worked on a lot of possible way to interact and flash ESP32. I gathered all the information from the boards we as a group worked in a table to see what are the possible and most effective way to program micro controllers.

Board Chip ToolChain WorkFlow/IDE Framework/Libraries Connection Code Time to compile & Flash Sketch memory Global variable memory Comment
MyBarduino ESP32 Esptools ArduinoIDE Arduino USB-FTDI Arduino 10s 198kb 13kb
MyBarduino ESP32 Esptools ArduinoIDE ESP library USB-FTDI C 10s 197kb 13kb
MyBarduino ESP32 Esptools ESP-IDF ESP-IDF GPIO C 10s 155kb 24.1kb
MyBarduino ESP32 Esptools PlatformIO Arduino GPIO Arduino 10s 266kb 13kb
MyBarduino ESP32 Esptools MicroPython MicroPython GPIO Python 10s N/A N/A
MyBarduino ESP32 Esptools MicroPython MicroPython USB-FTDI Python 10s N/A N/A
MyBarduino ESP32 Esptools MicroPython MicroPython WebREPL Python slow N/A N/A
Arduino UNO 328p Arduino AVR ArduinoIDE 674bytes
Lilytiny ATtiny85 Arduino AVR ArduinoIDE
Arduino PRO Mini 328p Arduino AVR + FTDI ArduinoIDE
Arduino PRO Micro 32u4 Arduino AVR ArduinoIDE
Leonardo 32u4 Arduino AVR ArduinoIDE
LilyPAD 32u4 Arduino AVR ArduinoIDE
ATTINY 45 FLEX Arduino AVR + ISP ArduinoIDE
TEENSY Custom TEENSY ARM TEENSYDUINO (Arduino Modified)

For each point of the table I can note the following as learning points :

  • Workflow : lots of workflows are available for all the board. One should choose the one he feels confortable with because at our programming level there is no much differences. If I had to choose one for ESP-32, ESP-IDF is nice because it fits my chip (ESP32) but Platformio interaction in the terminal is really smooth. So that would be one of those.

  • Framework : This is the environment of library you work with. In my case I worked a lot with Arduino and bare C with register interaction. Python was also involved. When working with AVR chip there is sense to use bare C to save on space. But when working with ESP32 it seems that even working only with C there is no much memory to save on because “side libraries” are to be loaded on the chip and the blink code will be a tiny part of it.

  • Connection : We saw several ways to connect to our boards ranging from USB-FTDI to Raspberry pi 4 GPIO pins passing by direct wiring connection on flexible board. I enjoyed working with Rpi4 GPIO pins as it avoid using an FTDI board and free a USB port.

  • Time to compile and flash : For ESP32 I noted that the flashing time is longer than for other board probably due to the extra features (wifi, bluetooth etc…). Also when working with ESP-IDF, the first build is really slow and I could see a lot of library were loaded.

  • Memory usage : For the ESP32 we were not capable to reach the value of AVR processor (factor of 1000). This is probably due to the programs needed to run all the extra features of the board. Also with Pyhon we could not fully evaluate the weight as the main part of the code is MicroPyhon which is loaded via the firware. The we only send the python code which has a low weight (few bytes)

Memory analysis

For information I place the memory output of each compiled file. ESP32_blink_01_FullArduino - Arduino 212.0 kb Sketch uses 198932 bytes (15%) of program storage space. Maximum is 1310720 bytes. Global variables use 13276 bytes (4%) of dynamic memory, leaving 314404 bytes for local variables. Maximum is 327680 bytes.

ESP32_blink_02_PinTranslation - Arduino 218.3kb Sketch uses 204892 bytes (15%) of program storage space. Maximum is 1310720 bytes. Global variables use 13436 bytes (4%) of dynamic memory, leaving 314244 bytes for local variables. Maximum is 327680 bytes.

ESP32_blink_02_PinTranslation_minicode - Arduino With delay : 211.1kb Sketch uses 198008 bytes (15%) of program storage space. Maximum is 1310720 bytes. Global variables use 13104 bytes (3%) of dynamic memory, leaving 314576 bytes for local variables. Maximum is 327680 bytes. No delay : 210.0 kb Sketch uses 197802 bytes (15%) of program storage space. Maximum is 1310720 bytes. Global variables use 13092 bytes (3%) of dynamic memory, leaving 314588 bytes for local variables. Maximum is 327680 bytes.

Blink Adrien - On ESP-IDF With/Wihout delay:182.7kb 24.1kb : 0x1000 /home/pi/Documents/00_Projects/01_FabAcademy/repository/adrien-laveau/docs/files/week08/blinkAdrien_ESP-IDF/build/bootloader/bootloader.bin 155.5kb : 0x10000 /home/pi/Documents/00_Projects/01_FabAcademy/repository/adrien-laveau/docs/files/week08/blinkAdrien_ESP-IDF/build/blinkAdrien.bin 3.1kb : 0x8000 /home/pi/Documents/00_Projects/01_FabAcademy/repository/adrien-laveau/docs/files/week08/blinkAdrien_ESP-IDF/build/partitions_singleapp.bin

Personal assignment

In the documentation I will present this week I experienced interacting with my ESP32 in the following ways.

  • Arduino IDE
  • Arduino pure C
  • make process - ESP-IDF
  • plateform IO
  • Raspberry : flash with GPIO
  • Micro Python

During the lectures we learnt that programming a micro-processor consists in several major steps :

  • Writing code
  • Preprocessing the code
  • Compiling the code
  • Link the compiled code with eventual libraries
  • Convert it to binary
  • Flash it in the microprocessor

Those steps can be done in A LOT OF different ways. During this week I will try to review some of them and understand the difference of each method. I will also try to remove the “abstraction layers” as much as I can to fully understand each process.

During Week6 Electronic Design I created a board based on the ESPRESSIF microchip. So my exmperimentations will be based on this board. I will use its documentation to go through the week :

ESP32 specificity

Flash mode

In order to be flashed the ESP32 has to be set in boot mode. To do so in the design we introduced 2 elements : * A microswitch to change the mode –> red * A button to reset the board (kind of unplug-plug the power source) –> green

IDE install
Hardware for reprogrammation

PinOut

My board is inspired on the Barduino Project from FabLab Barcelona. For this week I will use their visual pinout.

Barduino PinOut
Barduino PinOut

Datasheet Analysis

Introduction

The ESP32-WROOM-32 is a microcontoller that comes with extra features such as :

  • Wi-Fi
  • Bluetooth

The chip itself is based on a chip called ESP32-D0WDQ6. It has 2 CPU and the frequency can be adapted from 80MHz to 240MHz. And in case one is working on a low power requirement project, a co low-power co-processor can work instead of the CPU to do low power tasks (it is getting complicated …)

The ESP32 can manage a lot of peripherals such as :

  • SD Card interface
  • Ethernet
  • SPI
  • UART
  • I2S
  • I2C

Thoses protocols are interesting because I know that some LCD screen require I2C protocol to work. Also my final project might need to store data on an SD card if no internet is available.

Functional description

The ESP32 has 2 processors called “low-power Xtensa 32-bit LX6”.
The 32 bit information is important because it will impact our way to deal with registers. From ADAFRUIT website documentation I found out that the use the harvard-processor-architecture which is known to be better than the newmann one as data and program are stored in different memories and so avoid the “newmann bottle neck”.

The CPUs are called :

  • PRO_CPU : protocol CPU
  • APP_CPU : application CPU
Memory

The table below shows the memory available in the chip. Before giving the information I will try to explain with my words the use of each memory and to reproduce a memory map. Program memory (interesting link from microchip

  • ROM : Read only memory -> when creating the chip
  • EPROM : Erasable Programable Read Only memory -> need a window in the casing to be flashed with light !
  • EEPROM (flash) : Electrically Erasable Programable Read Only Memory -> when flashing the chip “by wire”

I start to think that we say “flashing a microcontroller” because when program the Flash/EEPROM memory (nor the ROM neither the EPROM)

Internal Memory

Memory type Quantity Description
ROM 448kB For the base code of the chip that will not be changed
SRAM 520kB Faster and more expensive than DRAM. For cache and internal register
SRAM in RTC - RTC FAST 8kB
SRAM in RTC - RTC SLOW 8kB
eFuse 1kB Used as One Time programming to set up specific infor,ation in the chip

External memory

Memory type Quantity Description
SPI Flash 4Mb Connected to GPIO6,7,8,9,10,11. They cannot be used an normal GPIO

Memory mapping

Memory Mapping
ESP32 Memory Mapping

Each CPU has a 32bit address space. It means an address is defined by a word of 32 bit long (that is 4,2 billions possible addresses! - 4Gb)

Cool video on how to desacralize memory address and physical location of the bit

Accessing the GPIO

What I am going to describe below took me around 15hrs to understand and to reach my goal of bliking the led without Arduino IDE. It was a first for me to read so deeply a datasheet and I want to share what I learnt. No everything is clear in my hea at the moment, but what I learnt allows me to fully remove Arduino code.

Peripheral Address Mapping - GPIO
Extract from, table 1-6 of datasheet. All GPIO addresses related register are stored in the range of addresses presented in the table below.
One concret example of that is the following.
In the C code of the ESP32 library for Arduino one can find the following line :

#define GPIO_OUT_REG (DR_REG_GPIO_BASE + 0x0004)
#define DR_REG_GPIO_BASE 0x3ff44000

Which means that :

  • GPIO_OUT_REG = 0x3ff44004
  • DR_REG_GPIO_BASE = 0x3ff44000

Both thoses values are within 0x3FF4_4000 and 0x3FF4_4FFF. It seems a small info but I was pretty happy to be able to “use” some info of the datasheet (table below).

Bus Type Boundary address Size Target
Data from 0x3FF4_4000 to 0x3FF4_4FFF 4 KB GPIO

Now how do we really access the GPIO. Well the datasheet contains a paragraph called “4 IO_MUX and GPIO Matrix” in which it explains the workflow to link peripherals (every sensor including GPIO handle by the chip and PADS (also called pins).

Definition (from what I understood)

Pads : physical pins Peripherals : all the sensors and GPIO capacity of the chip

The goal of this part is to route the proper peripherals in the proper pad and vice-versa. The chapter is broken in 2 parts which explain the diagram below depending if we are looking for an input or an output.

GPIO Matrix overview
GPIO Matrix overview

Peripheral Input via GPIO Matrix Below one can see that the function (register) GPIO_FUNCy_IN_SEL will link Peripheral signal Y with the GPIO set in its bit word.

Peripheral inputs
Peripheral inputs

Simple GPIO Input - as explaind in datasheet

The GPIO_IN_REG/GPIO_IN1_REG register holds the input values of each GPIO 
pad.
The input value of any GPIO pin can be read at any time without configuring 
the GPIO Matrix for a particular peripheral signal. However, it is necessary
 to enable the input in the IO_MUX by setting the FUN_IE bit in the
IO_MUX_x_REG register corresponding to pad X, as mentioned in Section 4.2.2.

Peripheral Output via GPIO Matrix

Peripheral outputs
Peripheral outputs

Simple GPIO Output - as explaind in datasheet

The GPIO Matrix can also be used for simple GPIO output – setting a bit 
in the GPIO_OUT_DATA register will write to the corresponding GPIO pad.
To configure a pad as simple GPIO output, the GPIO Matrix GPIO_FUNCx_OUT_SEL
 register is configured with a special peripheral index value (0x100).

To make it work I had to use the following functions found in soc.h (Arduino ESP32 library) :

// Set GPIO_FUNCx_OUT_SEL at 0x100 to confirm it is a GPIO Output
// One cannot just use the equality you have to use REG_WRITE and REG_WRITE
// Activation of the GPIO function
REG_WRITE(GPIO_FUNC18_OUT_SEL_CFG_REG, 0b100000000 | REG_READ(GPIO_FUNC18_OUT_SEL_CFG_REG));

// Set the GPIO_OUT_REG registry to 1 at bit 18 to  light up the LED using 
// either GPIO_OUT_REG or GPIO_OUT_W1TS_REG
REG_WRITE(GPIO_OUT_REG, ((1<<18) | REG_READ(GPIO_OUT_REG)));
or  
REG_WRITE(GPIO_OUT_W1TS_REG, ((1<<18) | REG_READ(GPIO_OUT_W1TS_REG)));

// Set the GPIO_OUT_W1TC_REG registry to 1 at bit 18 to switch off the LED
// This command is clearing the output register and has to be set at 1 not 0 as
// one (me...) would think.
REG_WRITE(GPIO_OUT_W1TC_REG, ((1<<18) | REG_READ(GPIO_OUT_W1TC_REG)));

Below one can see the binary representation of both registry :

GPIO_FUNCn_OUT_SEL
Registry : GPIO_FUNCn_OUT_SEL
GPIO_FUNCn_OUT_SEL
Registry : PIN_GPIO_OUT_REG

Even deeper

Another level of abstraction can be removed by using directly the functions REG_WRITE and REG_WRITE in C style.

(*(volatile uint32_t *)(GPIO_OUT_REG)) = (((1<<18) | GPIO_OUT_REG));

Instead of

REG_WRITE(GPIO_OUT_REG, ((1<<18) | REG_READ(GPIO_OUT_REG)));

But as I do not understand fully the volatile concept I let it aside for the moment.

Arduino

Arduino IDE

The Arduino IDE is more than a tool to work with Arduino. It is a C based framework that does a lot of things for you. Basically, all the steps mentionned earlier can be done in 1 click within the IDE.
And the great things is that if someone has created the code to deal with your chip and board, you can program it using Arduino IDE.

Below I will show you how to install Arduino on the Pi and take and a simple blink example and remove as much abstraction layer as possible.

Install it on Rpi4
  1. Go to Arduino Official webpage
  2. Download the Arduino IDE for ARM
  3. Then install it (following instruction from this web

    cd
    cd Downloads
    ls (to confirm it's here)
    tar -xf arduino-####-linuxarm.tar.xz (for me nightly-linuxarm.tar.xz) -> will extract the package
    sudo mv arduino-nighlty-linuxarm.tar.xz /opt --> move the files to the **opt** folder
    sudo /opt/arduino-nightly-linuxarm.tar.xz/install.sh --> will install the IDE
    

You can now enjoy the Arduino IDE :)

IDE install
Arduino IDE installed

The configuration I use for the ESP32 is the following one :

ArduinoConf
Arduino configuration for ESP32

Coding & Flashing with Arduino

Now I will work on various way to flash my ESP32 using Arduino IDE.

  • Full Arduino : pure Arduino code using the whole library
  • Direct PIN interation - LED only : I translate the Arduino command to pure C
  • Direct PIN interaction - LED and Button : Now everything is in C !

First iteration with Arduino IDE. I have just added some interaction with the button of my board compared to the basic example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void setup() {
  pinMode(19, INPUT);    // sets the digital pin 19 as input
  pinMode(18, OUTPUT);    // sets the digital pin 18 as output
  pinMode(13, OUTPUT);    // sets the digital pin 13 as output
}

void loop() {
  if (digitalRead(19) == LOW) {
    digitalWrite(18, HIGH);
    delay(1000);
    digitalWrite(13, LOW);
  } else {
    digitalWrite(18, LOW);
    digitalWrite(13, HIGH);
  }
}
Sketch uses 198932 bytes (15%) of program storage space. Maximum is 1310720 bytes. Global variables use 13276 bytes (4%) of dynamic memory, leaving 314404 bytes for local variables. Maximum is 327680 bytes.

Danger

To reach that point I literraly needed 10h of work… It is based on the research presented in the datasheet analysis. After doing this work I would to pay my respect to the persons who worked on the abstracion layers :)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
void setup() {
  REG_WRITE(GPIO_FUNC18_OUT_SEL_CFG_REG, ~0b111111111 & REG_READ(GPIO_FUNC18_OUT_SEL_CFG_REG));
  REG_WRITE(GPIO_FUNC18_OUT_SEL_CFG_REG, 0b100000000 | REG_READ(GPIO_FUNC18_OUT_SEL_CFG_REG));
  REG_WRITE(GPIO_ENABLE_W1TS_REG,((1<<18)| REG_READ(GPIO_ENABLE_W1TS_REG)));   
}

void loop() {
   REG_WRITE(GPIO_OUT_REG, ((1<<18) | REG_READ(GPIO_OUT_REG)));
   delay(1000);
   REG_WRITE(GPIO_OUT_W1TC_REG, ((1<<18) | REG_READ(GPIO_OUT_W1TC_REG)));
   delay(1000);
}
Sketch uses 204892 bytes (15%) of program storage space. Maximum is 1310720 bytes. Global variables use 13436 bytes (4%) of dynamic memory, leaving 314244 bytes for local variables. Maximum is 327680 bytes.

Now I will work at adding the button using register interaction to remove Arduino libraries.

In this part I have two functions
* The blinking LED on port 18 * A button commanded LED on port 13

For the LED 13 I spent a lot of time because it was not working even using the exact same command as for LED on pin 18. It is because the pin 13 does not act GPIO as default. One can see in the table below (extract from datasheet) that the GPIO 13 has :

  • MTCK as function 1
  • GPIO as function 2

IO_MUX pad summary
IO_MUX Pad Summary

So you have to set function 2 in IO_MUX_GPIO13_REG and then set the GPIO as OUTPUT.
Set bit 12-13 at 10 (function 3 = 0b10)

REG_WRITE(IO_MUX_GPIO13_REG,(~( 1 << 12 ) & REG_READ(IO_MUX_GPIO13_REG)));     
REG_WRITE(IO_MUX_GPIO13_REG,(( 1 << 13 )| REG_READ(IO_MUX_GPIO13_REG))); 
Enable the pad just in case

REG_WRITE(IO_MUX_GPIO13_REG,(~( 1 << 9 ) & REG_READ(IO_MUX_GPIO13_REG)));

The information comes from the datasheet

IO_MUX_x_REG
Registry : IO_MUX_x_REG

Here is the final code with the higlighted line dealing with what is explained before.

Also for reading the button state and using it in an IF statement I use both Bit shifting operators << and >>.

  • REG_READ(GPIO_IN_REG) >> 19 –> shifts my bit 19 to position 0
  • (1<<0) & REG_READ(GPIO_IN_REG) >> 19 –> check value of bit 1 using & operator
  • (1<<0) & REG_READ(GPIO_IN_REG) >> 19 == 0 –> comparing the result 1 or 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void setup() {
   Serial.begin(115200);
  //LED button
  //The part below tells the CHIP that the PAD has to use its GPIO function as by default it has another function
  REG_WRITE(IO_MUX_GPIO13_REG,(~( 1 << 12 ) & REG_READ(IO_MUX_GPIO13_REG)));     
  REG_WRITE(IO_MUX_GPIO13_REG,(( 1 << 13 )| REG_READ(IO_MUX_GPIO13_REG)));   
  REG_WRITE(IO_MUX_GPIO13_REG,(~( 1 << 9 ) & REG_READ(IO_MUX_GPIO13_REG))); 
  //We know set the pin a GPIO output
  REG_WRITE(GPIO_FUNC13_OUT_SEL_CFG_REG, 0b100000000 | REG_READ(GPIO_FUNC13_OUT_SEL_CFG_REG));
  REG_WRITE(GPIO_ENABLE_W1TS_REG,(( 1 << 13)| REG_READ(GPIO_ENABLE_W1TS_REG)));   

  //LED blink
  REG_WRITE(GPIO_FUNC18_OUT_SEL_CFG_REG, 0b100000000 | REG_READ(GPIO_FUNC18_OUT_SEL_CFG_REG));
  REG_WRITE(GPIO_ENABLE_W1TS_REG,(( 1 << 18 )| REG_READ(GPIO_ENABLE_W1TS_REG)));  

  //Preparing input (Button)
  REG_WRITE(IO_MUX_GPIO19_REG,((1<<9) | REG_READ(IO_MUX_GPIO19_REG)));   
    //FUN_IE bit = 9

}

void loop() {
   if((1<<0) & REG_READ(GPIO_IN_REG) >> 19 == 0 && (1<<0) & REG_READ(GPIO_OUT_REG) >> 13 == 0){
    Serial.println("ON");
    REG_WRITE(GPIO_OUT_W1TC_REG, (~(1<<13) & REG_READ(GPIO_OUT_W1TC_REG)));
    REG_WRITE(GPIO_OUT_W1TS_REG, ((1<<13) | REG_READ(GPIO_OUT_W1TS_REG)));
   } else if ((1<<0) & REG_READ(GPIO_IN_REG) >> 19 == 0 && (1<<0) & REG_READ(GPIO_OUT_REG) >> 13 == 1){
    Serial.println("OFF");
    REG_WRITE(GPIO_OUT_W1TS_REG, (~(1<<13) & REG_READ(GPIO_OUT_W1TS_REG)));
    REG_WRITE(GPIO_OUT_W1TC_REG, ((1<<13) | REG_READ(GPIO_OUT_W1TC_REG)));
   }

   REG_WRITE(GPIO_OUT_W1TS_REG, ((1<<18) | REG_READ(GPIO_OUT_W1TS_REG)));
   delay(500);
   REG_WRITE(GPIO_OUT_W1TC_REG, ((1<<18) | REG_READ(GPIO_OUT_W1TC_REG)));
   delay(500);

Sketch uses 204892 bytes (15%) of program storage space. Maximum is 1310720 bytes. Global variables use 13436 bytes (4%) of dynamic memory, leaving 314244 bytes for local variables. Maximum is 327680 bytes.

Below is the full code of the “Direct PIN Interaction - LED only” tab. One can notice the extensive use of the Serial.Print function for debugging. It was really interesting to check the bit state of the registry and confirm that some modification were properly done. Also you can check how the MC interprets the binary operations (<<, >>, && etc…)

Using Serial with Arduino IDE

  1. Activate the Serial protocol

    Serial.begin(115200);

  2. Print something to the serial

    Serial.println(“Something”) //Text information Serial.println(Boolean register,BIN) //Add the “BIN” tag to see it in binary

  3. Check the serial output using miniterm (you might have to press the “reset” button)

    python -m serial.tools.miniterm /dev/ttyS0 115200

Danger

Some registry are “WO = write only” and cannot be read, and return 0. I did not know that and got confused a lot while debugging. Take care !

void setup() {
  Serial.begin(115200);

  Serial.println("STAAAART");
  Serial.println(REG_READ(GPIO_FUNC18_OUT_SEL_CFG_REG),BIN);
  REG_WRITE(GPIO_FUNC18_OUT_SEL_CFG_REG, ~0b111111111 & REG_READ(GPIO_FUNC18_OUT_SEL_CFG_REG));
  Serial.println(REG_READ(GPIO_FUNC18_OUT_SEL_CFG_REG),BIN);
  REG_WRITE(GPIO_FUNC18_OUT_SEL_CFG_REG, 0b100000000 | REG_READ(GPIO_FUNC18_OUT_SEL_CFG_REG));
  Serial.println(REG_READ(GPIO_FUNC18_OUT_SEL_CFG_REG),BIN);
  Serial.println(REG_READ(GPIO_ENABLE_REG),BIN);
  Serial.println(( 1 << 19),BIN);      
  Serial.println( 1 << 19 | REG_READ(GPIO_ENABLE_REG),BIN);   

  Serial.println(REG_READ(GPIO_ENABLE_REG),BIN);
   REG_WRITE(GPIO_ENABLE_W1TS_REG,((1<<18)| REG_READ(GPIO_ENABLE_W1TS_REG)));   


}

void loop() {
  Serial.println("bonjour");   


   Serial.println(REG_READ(GPIO_OUT_REG),BIN);
   REG_WRITE(GPIO_OUT_REG, ((1<<18) | REG_READ(GPIO_OUT_REG)));
   Serial.println(REG_READ(GPIO_OUT_REG),BIN);



delay(1000);

   Serial.println(REG_READ(GPIO_OUT_W1TC_REG),BIN);
   Serial.println(REG_READ(GPIO_OUT_REG),BIN);
   REG_WRITE(GPIO_OUT_W1TC_REG, ((1<<18) | REG_READ(GPIO_OUT_W1TC_REG)));
   Serial.println(REG_READ(GPIO_OUT_W1TC_REG),BIN);
   Serial.println(REG_READ(GPIO_OUT_REG),BIN);

   delay(1000);


}

ESP-IDF Framework

Install and Use ESP-IDF

Links used for this part :

The explanation below are copied from the weblink above which is an extract of the offical web page of Espressif

  1. Getting some packages needed for ESP-IDF
    > sudo apt-get install git wget make libncurses-dev flex bison gperf python python-serial
  2. Create base directory
    > mkdir -p $HOME/esp32
  3. Download binary Toolchain for the ESP32
    ESP32 toolchain (32 and 64-bit) for Linux is available for download from Espressif website. Download this file, then extract it to the $HOME/esp.

    cd $HOME/esp32
    wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-61-gab8375a-5.2.0.tar.gz
    tar -xzf xtensa-esp32-elf-linux64-1.22.0-61-gab8375a-5.2.0.tar.gz
    export PATH=$PATH:$HOME/esp32/xtensa-esp32-elf/bin
    echo "export PATH=\$PATH:$HOME/esp32/xtensa-esp32-elf/bin" >> ~/.bashrc
    
  4. Get ESP-IDF from GitHub
    Clone ESP-IDF using git clone command. Framework will be downloaded into $HOME/esp/esp-idf. I strongly recommend cloning the repository to local computer because it’s frequently updated and It’s good to keep it up to date.

    cd $HOME/esp32
    git clone --recursive https://github.com/espressif/esp-idf.git
    export IDF_PATH=$HOME/esp32/esp-idf
    echo "export IDF_PATH=$HOME/esp32/esp-idf" >> ~/.bashrc
    
    Install dependencies

    cd ~/esp/esp-idf
    ./install.sh
    

    To be run all the time

    This will add the necessary path to the current terminal session

    . $HOME/esp32/esp-idf/export.sh

  5. Update framework ESP-IDF It’s a easy to keep framework ESP-IDF up-to-date with the following commands.

    cd $HOME/esp32/esp-idf
    git pull
    git submodule update
    
  6. Compile “blinky” example Now you can test the toolchain. Compile one of the examples from directory $HOME/esp-idf/examples.

    cd $HOME/esp32/esp-idf/examples/get-started/blink
    make menuconfig
    make 
    
    When using make for the first time a configuration menu appears which is the equivalent of the Arduino Tool menu where you can set up a lot of information such as the port for flashing, baud rate etc…

    ESP-DIF Menu
    ESP-DIF Menu

    The second time you use make the menu does not appear. If you want to see it again you have to run ‘make menuconfig’ then ‘make’

  7. Flash

    Once the code is compiled, the ‘make’ command tells you :

    To flash all build output, run 'make flash' or:
    python /home/pi/esp32/esp-idf/components/esptool_py/esptool/esptool.py 
    --chip esp32 --port /dev/ttyUSB0 --baud 115200 --before default_reset 
    --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m 
    --flash_size detect 
    0x1000 /home/pi/esp32/esp-idf/examples/get-started/blink/build/bootloader/bootloader.bin 
    24kb
    0x10000 /home/pi/esp32/esp-idf/examples/get-started/blink/build/blink.bin 
    164.3kb
    0x8000 /home/pi/esp32/esp-idf/examples/get-started/blink/build/partitions_singleapp.bin
    3kb
    **Total 191.3kb**
    

    From which the important informations can be seen :

    • –chip esp32 : flashed chip
    • –port /dev/ttyUSB0 : port where the board is plugged
    • –baud 115200 : baud rate to be used
    • your_path/blink.bin : the binary file to be flashed

    The flash process is using in both Arduino and ESP-IDF the tool esptool.py v3.1-dev.

    ESP-DIF Menu

    ESP-DIF Menu

Adapt my Arduino C code to ESP-IDF

  1. Parenthesis everywhere
  2. USe int ‘app_main(void) {‘ instead of ‘int main(void) {‘
  3. Add the following libaries
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "led_strip.h"
#include "sdkconfig.h"

Compare ISP-EDF and Arduino outputs

Arduino Output

Let’s strip down the commands.

python /home/pi/.arduino15/packages/esp32/tools/esptool_py/3.0.0/esptool.py
--chip esp32 
--port /dev/ttyUSB0 
--baud 921600 
--before default_reset 
--after hard_reset write_flash 
-z 
--flash_mode dio 
--flash_freq 80m 
--flash_size detect 
0xe000 /home/pi/.arduino15/packages/esp32/hardware/esp32/1.0.5/tools/partitions/boot_app0.bin
8kb 
0x1000 /home/pi/.arduino15/packages/esp32/hardware/esp32/1.0.5/tools/sdk/bin/bootloader_qio_80m.bin 
18.2kb
0x10000 /tmp/arduino_build_345595/ESP32_blink_03_PinTranslationAndButton.ino.bin 
200kb
0x8000 /tmp/arduino_build_345595/ESP32_blink_03_PinTranslationAndButton.ino.partitions.bin 
3kb
**Total 229.2kb**
python /home/pi/.arduino15/packages/esp32/tools/esptool_py/3.0.0/esptool.py
--chip esp32 
--port /dev/ttyUSB0 
--baud 921600 
--before default_reset 
--after hard_reset write_flash 
-z 
--flash_mode dio 
--flash_freq 80m 
--flash_size detect 
0xe000 /home/pi/.arduino15/packages/esp32/hardware/esp32/1.0.5/tools/partitions/boot_app0.bin 
8kb
0x1000 /home/pi/.arduino15/packages/esp32/hardware/esp32/1.0.5/tools/sdk/bin/bootloader_qio_80m.bin 
18.2kb
0x10000 /tmp/arduino_build_48376/ESP32_blink_01_FullARDUINO.ino.bin 
194.4kb
0x8000 /tmp/arduino_build_48376/ESP32_blink_01_FullARDUINO.ino.partitions.bin 
3kb
**Total 223kb**

Bare metal flashing (using ESP-IDF)

https://vivonomicon.com/2019/03/30/getting-started-with-bare-metal-esp32-programming/

“Bare metal flashing” could be done through ESP-IDF

Check that ESP tool chain is properly installed by checking the version

xtensa-esp32-elf-gcc –version

In the following folder lies bootloader_start.c which is the bootloader code. esp-idf/components/bootloader/subproject/main

call_start_cpu0 is the main function

Using xtensa-esp32-elf-nm build/bootloader/bootloader.elf we get where each part will be installed

… I leave it for a cold winter. I need a port I do not have. And I want to try more things.

Flashing with Raspberry GPIO

This is pretty useful if you have no FTDI or UPDI. For UPDI an extra resistance would be needed between TX and RX and is not presented here. Once the wiring is done one should use /dev/ttyS0 instead of /dev/ttyUSB0 and that’s all !

Wiring

One should connect to the board using the following GPIO of the Rpi4 :

  • 5V
  • GND
  • TX
  • RX

Comparing the pin layout of both Rpi4 and ESP32 I matched my wiring. Afterward in all my script I just have to use ttyS0 instead of ttyUSB0.

And it works !

ESP-DIF Menu
Flashing through GPIO of Rpi4

Micro Python

General guide lines

In this part I will try to use MicroPython and understand its advantages/disadvantages.

Downloadfirm ware

At https://micropython.org/download/esp32/ download the file adapted to your board.
For me : GENERIC : esp32-idf4-20210202-v1.14.bin

Install firmware

The following code line will guide you through the firmware intallation. Thoses steps have to be done everytime you use another system (Ex : Arduino, ESP-IDF…)

  1. Download the flashing tool

    pip3 install esptool

  2. Put the board in prog mode & push the rest button

  3. Erase the flash

    esptool.py –port /dev/ttyUSB0 erase_flash–> can take a while

  4. Press the reset button

  5. Flash the board with the firmware
    FTDI USB

    esptool.py –chip esp32 –port /dev/ttyUSB0 write_flash -z 0x1000 /home/pi/Downloads/esp32-idf4-20210202-v1.14.bin

    FTDI GPIO

    esptool.py –chip esp32 –port /dev/ttyS0 write_flash -z 0x1000 /home/pi/Downloads/esp32-idf4-20210202-v1.14.bin

First communication

I used several tool to communicate with the board flashed in Python. They are kind of light terminals.

With Picocom

sudo apt-get install picocom
picocom /dev/ttyS0 -b115200 //Push the reset button to start

Use it like a normal python console. Below a simple hello worl test.

ESP-DIF Menu
Flashing through GPIO of Rpi4

With miniterm

Use one of the following command depending on how is your board connected.

python -m serial.tools.miniterm /dev/ttyUSB0 115200
python -m serial.tools.miniterm /dev/ttyS0 115200

Over wifi WebREPL

To work over wifi some configuration steps needs to be put in place via wire.

  1. Connect via wire

    • With USB

      picocom /dev/ttyUSB0 -b115200 // conect via wire first

    • With Raspi4 GPIO

      picocom /dev/ttyS0 -b115200 // conect via wire first

  2. Run the webrepl setup. This function will guide you through the webrepl installation.

    import webrepl_setup

    WebREPL
    Setting up the WebREPL

  3. Connect to the Access Point
    Doing so would normally activate a Wifi Access-Point (AP). To which one should get connect to use the web interface. The access point is defined as follows :

    • ESSID : MicroPython-xxxxxx
    • password : micropythoN
    • IP : 192.168.4.1
  4. OR setup via your home Wifi I do not know why but the acces point is not working in my case. So I configured manually the ESP32 to connect to my house wifi.

    >>> import network
    >>> sta_if=network.WLAN(network.STA_IF)
    >>> ap_if=network.WLAN(network.AP_IF)
    >>> sta_if.active()
    False
    >>> ap_if.active()
    False
    >>> ap_if.ifconfig()
    ('192.168.4.1', '255.255.255.0', '192.168.4.1', '0.0.0.0')
    >>> sta_if.active(True)
    True
    >>> sta_if.connect('MyESSID', 'MyWfifiPassword')
    >>> sta_if.isconnected()
    True
    >>> ap_if.ifconfig()
    ('192.168.4.1', '255.255.255.0', '192.168.4.1', '0.0.0.0')
    >>> sta_if.ifconfig()
    ('192.168.0.31', '255.255.255.0', '192.168.0.1', '212.166.211.2')
    
  5. Access WebREPL To finally access the ESP32 via wifi once the connection is live you can go to this URL (http://micropython.org/webrepl/#IP.IP.IP.IP:8266/), the IP was detected using ap_if.ifconfig() in point 4. The IP of your chip will depend if your are using access point or home wifi.

    WebREPL
    Accessing WebREPL

Do Something !

Now that we are connected we can start doing stuff !
Direct communication

Either through wifi, picocom or miniterminal we can send commands to the ESP32.

Connections

Quick reminder to connect to ytour board with MicroPython

  • Picocom

    picocom /dev/ttyS0 -b115200

  • Miniterm

    python -m serial.tools.miniterm /dev/ttyS0 115200

  • WebREPL In your browser ‘http://micropython.org/webrepl/#IP.IP.IP.IP:8266/’

The following example switches on and off the LED linked to GPIO 2.

>>> import machine
>>> pin = machine.Pin(13, machine.Pin.OUT)
>>> pin.on()
>>> pin.off()
Executing Script & Flashing with MicroPython

Pyboard.py is a script that comes with the Micropython that allows to :

  • Execute a simple command
    Here we the board to compute and print 1+1

    python /home/pi/Public/micropython-master/tools/pyboard.py –device /dev/ttyS0 -c ‘print(1+1)’

  • Executing a script Here I send my python blink script to be run on the ESP32

    python /home/pi/Public/micropython-master/tools/pyboard.py –device /dev/ttyS0 /home/pi/Documents/00_Projects/01_FabAcademy/repository/adrien-laveau/docs/files/week08/ESP_32_MicroPython_blink.py

Flashing using Thonny IDE

The raspberry comes with an IDE called Thonny that allows a seamless interaction with your board.

  1. Set-up the interpreter

    • In Thonny go to “Tools option -> Interpreter”
    • Select Micropython(ESP32)

    Danger

    It does not work with GPIO. It has to be connected via USB

  2. Acces your board Once the interpreter chosen you can access your board and even its content. Then you can acces the content of the chip. You can add scripts and run them calling them from boot.py

    Danger

    For my first try I messed up because I called a script with an infinite loop (the blink test) from boot.py so the booting script never ends and I was not able to access the ESP32 in any way. I had to erase the flash a flash micropython once more.

Enjoy !

Plateform IO

Installation

Several methods are available and i will choose the one called Installer script which is the one recommanded in the documentation for the use I will make of it.

wget https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py -O get-platformio.py python3 get-platformio.py

Link platformio to the PATH

export PATH=$PATH:~/.platformio/penv/bin

Use Platformio

I will now follow the instruction to create a project, add some code, compile and flash.

First let’s look for the board containing the ESP32 that looks like my board.

pio boards ESP32

Pio outputs a list of board, the first one esp32cam looks fine. I will test with it as it fits the specs of my board.

PIO boards
ESP32 list of board in PIO

I initialize a project in the directory of my choice with the esp32cam.

pio init –board esp32cam

PIO init
Project initialization

Once this is done, let’s have a look at the content of plateformio.ini
In this case I have nothing to add, the basic settings seems to fit my need. We will see if a simple blink code compile and flash to confirm that.

PIO init
Our setup

The code for my application has to be located in /MyProject/src/ I will use the exact same code I used in Arduino IDE as the platform mentionned in the platformio.ini is Arduino.

To compile the code, place the terminal in the project directory and run the following command.

pio run

PIO compile
Compilation with pio

Finally to flash the compiled code run the following command :

pio run -t upload –upload-port /dev/ttyS0

Tip

As I flash using GPIO I use the option –upload-port to precise which port to use for flashing.

And the flash process goes without any issue.

PIO compile
Compilation with pio

Files

Arduino

Arduino with ESP32 registry

ESP-IDF

MicroPython

Lecture Notes

Fabacademy info

Hackmd link - Personal notes


Last update: May 18, 2021