Category Archives: microcontroller

RISC-V based ESP32c3 with ESP-IDF part 3, OLED screen, and potentiometer

Table of Content

* Introduction
* ADC Limitations on some ESP32 SoCs
* Potentiometer
* OLED I²C Screen
* Building the project and flashing


This piece of software was done for new year 2022, but procrastination helped me to delay the release of the tutorial, it continue the traditionnal (but with detailed explanations) LED blinking introduction tutorial. The goal of this tutorial is to learn to use potentiometer and little I²C screens (4 pins are I²C only, SPI versions use more pins) in EPE-IDF, with ESP32 microcontroller SoC based. I use here a really cheap (<5€) but powerful AI thinker ESP-C3-32S, that use an efficient low power RISC-V microcontroller.

Full schema with part 2 and 3

You can find the complete sources files and prebuild RISC-V firmware for ESP32-C3 on my files repository.

This example contain two main parts in the single file adc/esp32c3/adc/main/adc_dma_example_main.c, called in app_main(void), at the end of the file :
* One simple example single_read(NULL);, that make only one read of the state of the ADC, it uses ADC 1, channels 2,3,4) and ADC 2 (channel 0) and display datas on terminal.
* One more complex example continuous_read(NULL);, that reads 256 times the state of the channels and display them in the console, and then make continuous reading and change the onboard RGB Led blue colour light intensity.

The official documentation of the ADC.

ADC Limitations on some ESP32 SoCs

Limitations differs depending on ESP SoC, they have both 2 ADC, and one can’t be used when using WiFi:
* ESP32, based on Xtensa LX6 has 10 channels on ADC1 and 8 channels on ADC2, and ADC2 is used when Wifi is on.
* ESP32-S2 (no WiFi/BT) and ESP32-S3 (Wifi/BT), based on Xtensa LX7 (this last one has a RISC-V coprocessor for a more efficient ULP deep sleep mode), has 10 channels on both ADC, and ADC2 can’t be used when WiFi is on.
* ESP32-C3, based on RISC-V, ADC1 can’t be used with WiFi on, both ADC1 and ADC2 can’t be read simultaneously, you must read them alternately. ADC1 have 6 channels (6 pins) and ADC2 only one.

NodeMCU-series ESP-C3-32S-kit pinoutESP-C3-32S kit Pinout schema from JC François, with ADC pins in pink.

Full schema with part 2 and 3
Whole Breadboard montage with previous part tutorial and this one.

Potentiometer and OLED screen connexions
Connexions of potentiometer and OLED screen.


* The first top-left pin (ADC1_CH0 / ADC_CHECK in pink) is connected to the middle pin of the potentiometer (et right on the picture) using the white wire.
* The  3.3V , here 5th pin starting from the bottom left, but other 3.3V can be choosen, is connected to the left pin of the potentiometer (at right on the picture).
* The  GND , here 6th pin starting from the bottom left, but any ground pin can be used is connected using black wire to the right pin of the potentiometer.

V Red and GND black-blue breadboard lanes
Red lane and blue/black lane of the bread board.

Both  Vcc  and Ground are transiting by dedicated lane of the breadboard, on the top of the picture painted with red (meaning Vcc) and blue (meaning for black/Ground) lines. It is very important to keep black and red wire to these roles to avoid to burn components, any other colour can be used for data links. There is another lane at bottom. This is not clear on the picture, but the screen is connected on but on the first row of the inner part.

We need to include the adc.h headers, and we also add esp_log.h header here for debug purpose.

#include "esp_log.h"
#include "driver/adc.h"

Here are the presets used for potentiometer ADC (Analog-Digital Converter) in the source code.

/* ADC vars */

esp_err_t ret;
int adc1_reading[1] = {0xcc};
int adc2_reading[1] = {0xcc};
const char TAG_CH[][9] = {"ADC1_CH0", "ADC2_CH0"};

void init_adc()
  adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_0);
  adc2_config_channel_atten(ADC2_CHANNEL_0, ADC_ATTEN_DB_0);

OLED I²C Screen

An author made an interesting list of available colour display managed by ESP32 on Instructables.

I used the driver esp-idf-ssd1306 by nopnop2002 available on github (local archive)

There are 4 connector pins on the I²C only version:
*  GND , I use black wire and connect it to GND lane.
*  VCC , I use red wire and connect it to Vcc lane.
*  VCL  (sometimes VCK, VCLK as V clock), is for the clock signal, I choose green colour wire here, I connect it to  GPIO9 , at 4th pin starting from top right.
*  VDA  (VDA as V data), I choose white colour wire here, I connect it to  GPIO10 , at 6th pin starting from top right.

The SDA/SCL GPIO can be set by two way:

By editing the sdkconfig file at the root of the project and changing the following values to the values you want:


Or in the menu using:

make menuconfig

Then go to submenu SSD1306 Configuration ---> Then defining the number in (9) SCL GPIO number and (10) SDA GPIO number field.

By default, this application print the current settings in the monitor console via these functions:

        ESP_LOGI(tag, "INTERFACE is i2c");
        i2c_master_init(&dev, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO, CONFIG_RESET_GPIO);

The example of the driver is used for the screen. It pre-include the necessary headers files. ssd1306.h is the driver itself, font8x8_basic.h is a 8×8 pixels ASCII font set, and driver/i2c.h is the i²C protocol header, used to communicate with the screen microcontroller.

#include "ssd1306.h"
#include "font8x8_basic.h"
#include "driver/i2c.h"

I removed the demo, and set all the specific code in the #if CONFIG_SSD1306_128x64 section, as this is the model of my screen.

#if CONFIG_SSD1306_128x64
        top = 2;
        center = 3;
        bottom = 8; // 8 lines
        int n=200;
        int pos=0; // initial position = 0

Le main loop (while(n) {}):
I read ADC1 channel 0 (first pin) of the potentiometer and print the current value into the console

  adc1_reading[0] = adc1_get_raw(ADC1_CHANNEL_0);
  printf("chan[%d] 0x%x = %d\n", 0, adc1_reading[0],adc1_reading[0]);

Then I compute the current p position after a constant I predetermined, after test I seen that the specific potentiometer I use, as values in range [20 ; 2920]. And I have 8 text lines on screen, so I rounded to 3000/8 = 375. Output value / 8 compute the current line on screen.

  pos=adc1_reading[0]/375; // 20~2920  => need to calibrate 3000/8=375

Clearing the 8 text lines of the buffer, but the current line

  for (int i=0;i<8;i++) {
    if ( i != pos) {
      ssd1306_clear_line(&dev, i,false);

Printing 2022!! at the current line. the two space, allow to center a bit the text.

  ssd1306_display_text(&dev, pos, "  2022!!", 11, false);

And finally, wait a delay of 50 milliseconds before refreshing to avoid uselessly saturating processor and overloading.

  vTaskDelay(50 / portTICK_PERIOD_MS);

That's all ! We just have to build the project and put in on the board now.

Building the project and flashing

Build the example for AI thinker ESP-C3-32S

Initialising esp-idf:


Then go the the project root:

cd myproject/ set-target esp32c3

If you have the following error:

Adding "set-target"'s dependency "fullclean" to list of commands with default set of options.
Executing action: fullclean
Directory '/data/arc/esp/esp-idf/test/adc/esp32c3/adc/build' doesn't seem to be a CMake build directory. Refusing to automatically delete files in this directory. Delete the directory manually to 'clean' it:

You simply need to clean build subdirectory if it exists

rm -R build
mkdir build

and in any case to create the CMake:

cd build
cmake ..
cd ..

Then configure the project for your SoC target, in ESP32-C3 case: set-target esp32c3

If you need to change some settings of your porject, like GPIO ports for screen driver, you can edit the sdkconfig file or use make menuconfig now.

you will not have to redo all this procedure at each rebuild now, you can play with source code and build or rebuild/flash with the following last command: build flash monitor
You can quit the monitor by making the CTRL + ] keys combination.

Amusons nous avec un bouton et des LEDs sur une carte NodeMCU basée sur un ESP32-C3 (microcontrôleur RISC-V) avec ESP-IDF (FreeRTOS)

* This post is also available in English.


* Introduction
* Matériel : Le ircuit
** Composants
** Breadboard (planche à pain)
** Choix des ports GPIO et de leur broches sur la carte
** partie LED
** Résistances
** Partie bouton
* Le logiciel
** Initialisation
** Boucle principale
** Routine de Service d’intérruption (ou ISR, Interrupt Service Routine)
** Supprimer l’effet de rebond (Debouncing)
*** Chronomètre ESP


Mise à jour: J’ai écris, à la suite de cet article, un autre enseignant l’utilisation d’un potentiomètre et d’un écran OLED..

Suite à la mise à jour d’ArchLinux de python 3.9 vers 3.10, les outils ont du être réinstallés en effectuant :

cd ~/esp/esp-idf
git pull
git submodule update --init --recursive
./ esp32c3

Si vous n’avez jamais utilisé ESP-IDF, vous pouvez lire l’article précédent, d’introduction à ESP-IDF sur les ESP32-C3 basées sur du RISC-V, comment l’installer et lancer l’environment pour compiler et flasher le code. J’ai également écrit un article à propos de l’utilisation d’ESP32-C3 avec le système embarqué POSIX, Apache NuttX , mais il sera inutile ici.

Cet article concerne, à propos le développement, sur ESP32 (plus spécifiquement sur une carte NodeMCU basée sur un ESP32-C3 à moins de 3.5 €, mais il devrait fonctionner à peu près à l’identique sur les autres cartes basées sur de l’ESP) :
* Comment faire clignoter une LED externe en utilisant les ports dits GPIO, en expliquant comment déterminer la tension (en volts, V) et l’intensité (en ampères, A) nécessaires au fonctionnement de la LED, ainsiq que le calcul de la résistance nécessaire, par différents moyens.
* Des explications sur les bandes de couleur des résistances et le calcul des résistances montées en parallèles. Je donne également un lien vers un logiciel libre et à source ouverte que j’ai écrit, pour aider à calculer les résistances nécessaires (en fonction du type de LED, et de l’intensité désirée).
* Comment connectet un commutateur externe aux probches GPIO, et la résistance nécessaire. Comment recevoir et gérer de la bonne façon son état. Notamment en supprimant l’effet de rebond, (debouncing) liée à la pression physique d’un humain sur le bouton, ainsi que l’utilisationd el’interruption logicielle (c’est une tâche plus facile qu’il n’y paraît).
* Comment faire clignoter la LED RVB incluse et l’arrếter/démarraer en utilisant un interrupteur, de façon asyncrone.

Matériel : Le Circuit

Après 20 ans sans avoir pratiqué l’électronique et quelques recherches sur la façon de créer des cicrcuits, pour une LED et un bouton, j’ai trouvé différents tutoriaux incomplets pour certaines parties. J’ai finallement trouvé un article donnant des eplixations pour cette carte, mais en utilisant Arduino, et avec pas mal d’erreur importantes. Une résistance trop forte, et au mauvais endroit. Après avoir réuni les informations de plusieurs sources et après pas mal de tests, j’ai décidé d’écrire un tutoriel complet pour les débutants comme moi. Cela pourrait m’aider à comprendre de nouveau et retrouver facilement toutes les bases lorsque nécessaire, et j’espère qu’il sera utiles pour d’autres également.


Un outil simple pour tester et apprendre les circuits électroniques est une planche à pain (en anglais breadboard), elle permet de tester sans avoir à souder quoi que ce soit. Elle peut donc également être utilisée par des enfants, comme une tension de 3,3 V alimenté par les 2,4 A maximum d’un USB 2 ou 3 n’est pas dangereuse du tout.

* Nous avons également besoin d’une LED de 5 mm, que l’on peut trouver dans des vieux cercuits électroniques si besoin (plutôt que de les jeter) ou pour vraiment pas cher, nous utilisons ici une LED rouge qui est parfaite pour représenter un état allumé (ON) ou éteind (OFF) et, qui, avec une tension de 1,8 V peut être gérée par une carte fournissant une tension d’alimentation de 3,3 V.
* Nous avons également besoin d’un bouton de commutation. Un capuchon est plus confortable mais n’est pas indispensable pour le faire fonctionner.
* Nous avons enfin besoin d’une résistance, si vous en avez de 75 ohms (75 Ω), ou une de 100 Ω et une de 330 Ω, c’est parfais, sinon une de 100 ohm seule replira relativement bien cette tâche. De 100 à 330 Ω il y aura toujours de la lumière, mais ça ne sera probablement pas très brillante.

Vous pouvez également utiliser une résistance de 10 KΩ pour le bouton, appellée pull-up (ou pull-down) pour avoir un signal plus précis, mais une résistance externe n’est pas nécessaire avec ce type de carte qui contient déjà des résistances pull-up/pull-down internes, c’est à la fois plus efficace et il est possible de les dés/activer en logiciel avec la fonction esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull). Nous n’aurons pas besoin d’utilier le mode Open drain (drainage ouvert), utilisé avec les périphériques SPI, où les pull-up/pull-down sont utilises pour changer les signos actifs/inactifs.

Breadboard (planche à pain)

half width Breadboard
La planche à pain (en anglais Breadboard) est une carte vraiment pratique, qui permet de tester les circuits, sans faire de soudure. N’importe quel type de composant (sauf les composants à montage en surface), peut être facilement inséré ou retiré, et les contacts fonctionnent directement. Les trous sont espacés selon les standards de l’électronique de 2.54mm, soit 1 pouce des anciennes unités impériales britanniques, comme l’électronique moderne à principalement été développée aux États-Unis d’Amérique, et c’est le seul pays au monde qui utilise encore ce système complexe. Le Système International (SI), créé pendant la révolution française, est bien plus facile pour tout calcul en physique.

Breadboard de demi-taille avec les liens électriques internes représentés
En marron, les liens électriques placés à l’intérieur de la breadboard.

La carte fait de plastique comporte des liens électriques en son milieu. Ils sont divisés en trois parties ; Une partie centrale, large, et deux parties latérales.
* Les deux parties latérales sont simplement des longues lignes utilisées en principe pour la masse (GND) et l’alimentation (V ou Vcc). La ligne bleue représente la masse et la rouge le courant, et sont tracés afin d’éviter de mélanger les deux et de créer ainsi des courts-circuits. Le fait d’avoir une dorsale VCC+GND sur chaque côté est parfaite pour l’utilisation avec des cartes de type microcontrôleurs qui comportent souvent à la fois des broches d’alimentation en 5 V et en 3,3 V ou bien pour avoir un accès plus facile aux broches des 2 côts de la carte. Les cartes les plus efficaces ne comportent que des sorties 3,3 V.
* Dans la partie du milieu, les liens électriques sont perpendiculaires à ceux ddes côtés, et sont séparés au centre. ils sont principalement utilisés pour placer les composants électroniques. Des symboles, tels que des conteurs de trous, tous les 5 trous, et des lettres aux extrêmités, sont inscrits pour plus facilement monter un circuit à partir d’un patron.

Choix des ports GPIO et de leur broches sur la carte

Voici le brochage (en anglais « pinout ») de la carte, nous avons besoin de choisir les ports utilisés pour le circuit, certaines broches comportent des GPIO (de l’anglais General Purpose Input/Ouput, entrée-sortie à but généraliste), dont ceertaines ne supportent que des signaux numériques (0 ou 1), parfois des signaux analogiques, avec soit des convertisseurs analogique-numérique (ADC), numérique-analogique (DAC) ou les deux à la fois. Certaines borches permettent de fournir du courant aux composants électroniques (les ports 3,3 V en rouge sur l’illustration) Attention à ne pas les connecter directement aux ports GPIO, vous pourriez éndomager définitivement votre carte..

Brochage du NodeMCU ESP32-C3-32S-kit
Brochage du NodeMCU ESP32-C3-32S-kit

Voici la carte du brochage pour une carte NodeMCU ESP32-C3-32S-kit. Merci à J-C François pour ce dessin.

Vous pouvez générallement les trouver dans la documentation de votre carte. Il n’est cependant pas toujours facile de trouver ce genre de schéma, mais ils sont géénralement fournis par le producteur de la carte. La liste des GPIO des cartes ESP32-C3 sont dans la documentation, mais chaque fabriquant de carte peut décider de les lier à différentes broches.

Nous choisissons ici la GPIO 1 (GPIO_NUM_1 dans l’API) pour le bouton, et la broche GPIO 2 (GPIO_NUM_2) pour la LED. Pour cette carte en particulier, il s’agit réspectivement de la seconde et de la troisième broche, sur le côté gauche, en partant du haut.

Dans le code source, comme nous le verrons plus tard :


Partie LED

L'anode (+) et la cathode (-) sur un symoble diodeNous allons utiliser ici une LED rouge. LED signifie en anglais « light-emitting diode » on le traduit en français par DEL (diode electro-luminescente), il s’agît donc d’un type spécial de diode émettant de la lumière. Une diode ne fonctionne que dans une seule direction, nécessite une tension minimum déterminée pour fonctionner, et ne doit pas être alimenté au deçu d’une certaine tension, afin d’éviter de l’endommager. Si elle est alimentée dans la mauvaise direction (et pas trop fort), rien ne se produit. Cette propriété est très utiles dans un grands nombre de cas, mais ça n’est pas le sujet ici.

Symboles Diode et LED
Symboles diode (à gauche) et LED (avec des flèches)s.

La pate Anode est plus longue que celle de la cathode
La pate de l’anode est plus longue que celle de la cathode, Des LEDs posés sur une surface plane, roulent généralement d’elles même jusqu’à se mettre sur la surface plate.

le tour du bas de la LED est plat du côté de la cathode
Le contour du bas de la LED est plat du côté de la cathode.

Pour la partie LED, nous avons besoin d’une résistance (la majorité des tutoriels que j’ai trouvé indiquaient par erreur 330 Ω). les LEDs fonctionnent autour de différentes plages de tension. Aux alentours de 10 à 50 mA, utiliser 20 à 30 mA est une zone sécurisée en général, mais attention, elles utilisent parfois moins de mA. En général, avec une tension inférieure à 20 mA, la LED ne sera pas très brillante. La tension dépend de la couleur de la LED. Lors des tests, essayer de rester au milieu de ces tensions afin de ne pas brûler votre LED. Le mieux reste de suivre les spécifications du constructeur de votre LED. Il est parfois difficile de trouver ces informations et les valeurs changent d’un fabriquant à un autre. Mais en raison des composants chimiqus utilisés (cela pourrait changer en fonction de la rareté des matériaux), les valeurs limites sont situées aux alentours de :
* IR (infrarouge) 1,2 à 1,6 V
* rouge 1,8 à 2,1 V
* Orange/jaune 1,9 à 2,2 V
* Vert 1,8 à 3,1 V
* Blanc/UV (ultra-violet) 3 à 3,4 V
* Bleu 3 à 3,7 V
* RVB, chaque broche correspondant à la tension propre à sa couleur, se référer à la tension de la couleur dans au dessus (ou encore mieux, aux spécifications du fabriquant).

Vous pouvez tester la tension en avant (« Forward Voltage ») d’une LED à l’aide d’un multimètre numérique. En avant, parce que c’est la direction dans laquelle la LED s’illumine.

Multimètre numérique en position de test de diode
Multimètre numérique en position de test de diode.

Sélectionnez le mode diode du multimètre (comme sur l’image ci-dessus), et touchez l’anode (pate longue, du côté arrondi de la base de la LED) avec le connecteur rouge, et la cathode (pate courte, du côté de la base plate de la LED) avec le connecteur noir.

Nous utilisons une LED rouge, qui nécessite environ 1,8 V. L’ESP32-C3 dispose de sorties 3,3 V, il y a donc une différence de :

3,3 - 1,8 = 1,5 V

Nous avons donc besoin d’une résistance pour éviter une surtension de la LED.


D’après la loi d’Ohm, U = RI, où U est la tention (envolts ou V), R est la résistance (en Ohms ou Ω) et I l’intensité (en ampères ou A). Donc :

R = U/I
 1,5/0,02 = 75 Ω

Nous choissons la résistance la plus proche, ayant une résistance égale ou supérieure à ce résultat.

Les résistances sont peintes avec des anneaux de couleur indiquant leur résistance. Il y a en général 4 ou 5 anneaux (ou bandes). Ils doivent être lus de gauche à droite et représentent : résistance (2 bandes), multiplicateur (une bande) et pourcentage de tolérence (1 ou 2 bandes). Voici un bon calculateur de restistance à 5 bandes.

Une bon moyen mnemotéchnique de retenir l'ordre des couleurs est en français "Ne Mangez Rien Ou Je Vous Brule Votre Grosse Barbe". for value rings they start by 0, then 1, etc..., for multiplier by 1, then 10, etc....

Black (noir) 0 0 x1
Brown (marron) 1 1 x10 ±1%
Red (rouge) 2 2 x100 ±2%
Orange (orange) 3 3 x1000=x1K
Yellow (jaune) 4 4 x10K
Green (vert) 5 5 x100K ±0.5%
Blue (bleu) 6 6 x1000K=x1M ±0.25%
Violet (violet) 7 7 x10M ±0.1%
Gray (gris) 8 8 x100M ±0.05%
White (blanc) 9 9 x1000M=1G x1
Gold (or) ±5%
Silver (argent) x0.01 ±10%

Attention à la lumière, lorsque vous regardez les bandes, des bandes peuvent êtres confondues sous une lumière inapropriée, et peuvent avoir des conséquences désastreuses pour votre matériel, en particulier si il s'agît de la couleur du multiplicateur.

Résistance prises au flash à gauche et à lumière ambiante à droite
Photographie de résistances prisent avec un flash à gauche et une lumière ambiante ombrée à droite. Comme on peut l'observer ici sur la résistance bleue à gauche, le second anneau est peint en orange, mais il semble marron avec l'ombre de la lumière naturelle à droite.

Si vous avez des doutes sur leur valeurs, vous pouvez toujours utilisé un multimètre numérique sur leur position résistance, dénotée par le caractère grec Omega (Ω) utilisé pour les Ohms.

Multimètre nuimérique en position de test de résistance
Multimètre nuimérique en position de test de résistance

Certains suggèrent 330 ohms avec 5 V, c'est vraiment trop, mais même pour 3,3 V, il en résulte

1,5/330 = 0,01 A = 1 mA

1 mA est 1/20e de la lumière idéale, cela illuminera tout de même la LED, mais avec une intensité très faible.

J'ai une résistance 47 ohms, mais il en résulterait une valeur un petit peu trop élevée :

1,5 / 47 = 31.91 mA

Dans cette vidéo, la différence est visible sur la lumière ambiante, comme la caméra fait le point lumineux sur la LED. Ici, 330 ohms (alimentant donc à LED avec 1 mA) et 100 ohms (l'alimentant en 15 mA) les résistances sont connectées en parallèle, une meilleur solution (voir plus bas), puis 330 ohms uniquement. On peut voir que la lumière ambiante change seulement parce que le capteur du téléphone n'est pas en mode HDR. Cette différence de lumière démontre que la LED n'est pas assez alimentée.

J'ai développé un outil simple de calculat de résistance à l'aide de TIC80 en langage Lua. Vous pouvez l'utiliser en ligne dans votre navigateur tel quel (version utilisant WASM) dans la page en lien ou bien la télécharger, sur la même page pour l'utiliser sur votre ordinateur ou téléphone, c'est à source ouverte, sous licence GPLv3 :

Screenshot de resisor_calculator

Mise à jour du 28 décembre 2021 : J'ai découvert après avoir écrit ce billet, que la fameuse suite logicielle libre et à source ouverte de conception électronique, KiCad contient un outil appelé PCB Calculator permettant de calculer un grand nombre de choses liées à l'électronique, tels que les résistances et comporte une table de couleur des résistances. La version 6.0.0 d Kicad a été publiée le 25 décembre et contient plusieurs années de travail et d'importantes éméliroations.

Une autre loi d'Ohm dit que Lorsque les résistances sont liées en parallèle appelée « loi des résistances en parallèle », le calcul est :

1/R = 1/R₁ + 1/R₂ + ... + 1/R₉ + ...


R = 1 / (1/R₁ + 1/R₂ + ... + 1/R₉ + ...)

Et nous avons avec les résistances de 330 Ω et 100 Ω en parallèle :

R = 1 / (1/100 + 1/330) = 1 / 76.744 Ω
I = 1.5/76.744 = 0.01954 A = 20 mA

C'est vraiement très proche de la perfection.

Cette formule fait tout en une seule fois :

I = U * 1 / (1/R₁ + 1/R₂ + ... + 1/R₉ + ...)

Donc, ici :

1.5 / (1/100 + 1/330)

J'ai également crée un simple outil de calcul en ligne de commande pour les résistances en parallèle, disponible ici en plus de la calculatrice à résistence en tic80. Je dois toujours implémenter, le calcul du montage en parallèle dans la version Tic-80, et un outil de caulcul de résistance unique dans la version en ligne de commande.

Partie bouton

résistance Pull-up
Pour le bouton, le principle est juste un commutateur, il coupe le courant par défaut et crée un court-circuit lorsque le bouton est pressé. Ce commutateur est, sur le circuit, entre l'une des broches GPIO et une broche de la masse (GND). En raison du bruit sur le signal, une résistance pull-up/pull-down est nécessaire, afin de stabiliser le signal. Une résistance Pull-up/pull-down ajoute juste un courant très faible au court circuit, Une résistance de 10 KΩ connectée à la broche 3,3 V est utilisée à cette fin. Elle est appellée pull-up (tire vers le haut) si la résistance est connectée entre le bouton et la broche GPIO (3.3 V également), ou pull-down (tire vers le bas), si elle est connectée entre le bouton et la masse.

De nos jours, la majorité des cartes à microcontrôleurs, telles que les carte basées sur un ESP32-C3 comporte des résistances pull-up/pull-down internes. Elles peuvent être activée par logiciel.

Les pates de connexion du bouton de commutation peuvent être contre-intuitives à première vue, mais les pates su mêmes côté ne sont pas connectées entre elles, chauqe pate est connectée avec celle du côté opposé. Pour être plus clair, sur le schéma représentant la résistance pull-up, les deux pates à gauche sont toujours connectées ensemble et les deux pates à droites sont connectées ensemble, Le bouton établi la connexion entre les 2 pates de gauche et les 2 pates de droite.

Le logiciel

Nous voulons gérer :
* Une boucle principale qui fait clignoter la LED RVB de la carte.
* Une LED externe rouge qui s'illumine lorsque le clignottement s'arrête.
* Un bouton commutateur, qui peut à n'importe quelle moment, de façon asyncrone, commuté entre ces deux états. Ce bouton ne peut être vérouillé en bas, nous changeons donc d'état à chaque fois qu'il est enfoncé. Le relacher n'a aucun effet.

Pour gérer tout cela de façon asyncrone, nous avons besoin d'interruptions.
* Une interruption chronométrée afin d'attendre entre chaque pas du clignottement de la LED sur la carte.
* Une interruption lorsque le commutateur on/off est pressé, pour changer d'état.
* À ce niveau de développement, nous avons également besoin d'un interupteur par chronomètre asyncrone, utilisé pour ce que l'on appelle anti-rebond (debouncing en anglais). Lorsque le bouton est pressé, il rebondit, physiquement sur le contact et le contact passe plusieurs fois entre l'état on/off. Le même effet se produit au niveau plus bas qu'est le niveau électrique, mais le courant analogique, est géré par les composants électroniques, nous pouvons donc voir à notre niveau le courant avec un état logique numériuqe 1 (allumé/on) ou 0 (éteind/off).

Il y a un exemple inclus avec une file d'attente dans ESP-IDF, mais son but n'étiat pas très clair pour moi. Après la gestion du commutateur, Je l'ai regardé de nouveau, il permet de tester la file d'attente et les interruptions, en connectant simplement 2 broches à 2 autres broches, 2 broches envoient le signal, deux autres le reçoivent, il en suit une sortie sur le terminal série afin de comprendre ce qu'il se passe, c'est aussi très utile. Quoi qu'il en soit, après avoir pas mal recherché comment utiliser le bouton commutateur, j'ai trouvé ce sujet m'a beaucoup aidé pour la partie logicielle mais n'était pas complète, en particulier en ce qui concerne le debouncing. J'ai donc écrit ici une versionqui fonction et la façon dont elle fonctionne. Toutes les fpnctions à propos des GPIO dans ESP-IDF sont décrites ici.

J'ai effectué une copie de cet exemple esp-idf/examples/get-started/blink comme projet de départ. Voir l'article rpécédent à propos d'ESP-IDF à ce sujet, et comment le compiler.

Copiez simplement l'exemple de clignottement, et remplacez blink/main/blink.c par ma version, que vous pouvez télécharger ici.

Vous pouvez, comme première aide, le compiler et le flasher pour commencer, vous n'aurez alors plus qu'à remplacer le fichier blink.c, puis à le compiler et le flasher de nouveau.

Pour initialiser l'environnement pour de l'ESP32-C3 :

. $HOME/esp/esp-idf/ set-target esp32c3

Puis, pour le compiler et flasher (Vous pouvez également vous limité à la compilation et au flashage en fonction de vos besoins) : build flash


Dans cet exemple, définir state = 1 (on) fait clignoter la LED interne et la LED rouge à l'opposé, lorsqu'il est à 0 (off).
* Choisissez les ports GPIO utilisés, remettez à zéro ces GPIO, réglez celle des LED comme sortie (output), et le bouton comme entrée (input)
* Initialisez les interruptions et chronomètres pour le bouton, nous définissons à POSEDGE (positive edge, bord positif), cela correspond au moment ou le courant monte de 0 à 1, lorsque la liaison de contact devient active dans le bouton.

Dans le projet blink (clignottement) par défaut, la LED interne bleue est définie comme BLINKING_LED, nous allons à la plce commencer de sa GPIO interne, et faire de même pour les autres couleurs de la LED RVB interne (respectivement R=3. V=4. B=5). J'utilsie ici la broche du connecteur GPIO1 pour le bouton et GPIO2 pour la LED externe (ligne 19).

#define RED_GPIO   GPIO_NUM_3


Nous définissons une variable appelée state avec état de départ à 1. 1 = on, 0 = off (ligne 31) :

// set initial state to blinking led on
static int state = 1;

Nous avons besoin d'initialiser toutes les broches que nous allons utiliser (ligne 61):

  gpio_reset_pin(RED_GPIO); // reset internal RGB LED GPIO
  gpio_reset_pin(EXTERN_LED); // reset external GPIO

On crée alors le chronomètre, en envoyant à la fonction appropriée la structure préalablement définie (ligne 67).

  // create the timer
  esp_timer_create(&debounce_timer_args, &debounce_timer);

Nous définissons toutes les caractéristiques de la GPIO du bouton. Peut être défini en dehors du code, et envoyé à la fonction gpio_config (ligne 70).

  if (ISR_MODE == 1) {  // Interrupt mode
    gpio_config_t btn_conf;
    btn_conf.intr_type = GPIO_INTR_POSEDGE;  // Interrupt at Posedge only
    btn_conf.mode = GPIO_MODE_INPUT;           // Use INPUT mode
    btn_conf.pin_bit_mask = EXTERN_BUT_MASK;   // MASK of the button pin
    btn_conf.pull_up_en = GPIO_PULLUP_DISABLE;    //Disable pullup
    btn_conf.pull_down_en = GPIO_PULLDOWN_ENABLE; //Enable pulldown
    gpio_config(&btn_conf); // send config
  } else {               // Naive mode
    gpio_set_direction(EXTERN_BUT, GPIO_MODE_INPUT);
  printf("button configured\n");

On règle à présent l'interruption associée au bouton, à la GPIO EXTERN_BUT au gestionnaire de fonction isr_button_pressed() (ligne 83).

  if (ISR_MODE == 1) {
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); // install GPIO interrupt
    gpio_isr_handler_add(EXTERN_BUT, isr_button_pressed, (void*) EXTERN_BUT); //Add handler of interrupt
    printf("Interrupt configured\n");
  } // end of ISR initialistaion

On règle alors la direction en sortie (OUTPUT) pour la LED et change la LED rouge externe à 1 - state = 0, ou off, on ne définit pas les 3 LEDs internes à off, comme elle vont l'être juste après, au début de la boucle (ligne 89)

  /* Set the LED GPIO as a push/pull output */
  gpio_set_direction(BLUE_GPIO, GPIO_MODE_OUTPUT);
  gpio_set_direction(RED_GPIO, GPIO_MODE_OUTPUT);
  gpio_set_direction(GREEN_GPIO, GPIO_MODE_OUTPUT);
  gpio_set_direction(EXTERN_LED, GPIO_MODE_OUTPUT);
  printf("LED output configured\n");

  gpio_set_level(EXTERN_LED, 1 - state); // 1-1=0 1-0=1

Boucle principale

Dans cette boucle infinie (while(1)), On allume simplement, une couleur de la LED, attendons un peu en utilisant l'interruption, afin d'éviter de consommer de l'énergie pour rien, puis on éteind la LED. Quelques informations à propos de l'état courant sont envoyées à la console série, telles que l'état du système (signifiant si la LED interne (1) ou externe rouge (0) est allumée), à l'arrivée en début de boucle.

Nous choisissons ici de créeer une boucle et d'envoyer la valeur de l'état (state) à la LED, puis d'attendre via un chronomètre (timer), et enfin de changer la valeur de la LED à 0 afin de l'éteindre. Il aurait été mieux d'arrêter la boucle dès que le système est arrêté et d'attendre que l'état du système soit de nouveau à on avant de boucler de nouveau.

La boucle commence par écrire l'état actuel et étteindre la LED RVB (ligne 98):

while(1) {
  printf("starting cycle by turning off the LEDs\n");
  gpio_set_level(RED_GPIO, 0);
  gpio_set_level(GREEN_GPIO, 0);
  gpio_set_level(BLUE_GPIO, 0);

Puis, si nous somme dans le mode naïf (ligne 104), sans interuption, récupérons l'état du GPIO associé au button à l'aide de la fonction gpio_get_level(), et dans tous les cas, l'affiche (ligne 107). Cela permet de voir si l'on a bien connecté le bouton à la bonne broche de GPIO. On attend alors 1 seconde (1000 millisecondes, ligne 108):

  if ( ISR_MODE == 0 ) { // naive mode
    state = gpio_get_level(EXTERN_BUT);
  printf("state=%d\n",state); // display current state on console
  vTaskDelay(1000 / portTICK_PERIOD_MS); // wait with lights off

On allume alors la LED bleue interne, attend 200 millisecondes (0,2 s) puis l'eteind (ligne 110):

  gpio_set_level(BLUE_GPIO, state); // light on blue if state up
  vTaskDelay(200 / portTICK_PERIOD_MS);
  gpio_set_level(BLUE_GPIO, 0);     // light off blue

On fait alors la même chose avec la LED interne verte (ligne 114), puis la rouge (ligne 118), puis la rouge et la verte en même temps, donnant une lumière de couleur jaune (line 122). Nous attendons pour la dernière fois du cycle de la boucle pendant 200 ms (ligne 124) le cycle recommence alors en éteignant toutes les LEDs internes (ligne 100, voir au dessus) :

  gpio_set_level(GREEN_GPIO, state);// light on green
  vTaskDelay(200 / portTICK_PERIOD_MS);
  gpio_set_level(GREEN_GPIO, 0);    // light off green

  gpio_set_level(RED_GPIO, state);  // light on red
  vTaskDelay(200 / portTICK_PERIOD_MS);
  gpio_set_level(RED_GPIO, 0);      // light off red

  gpio_set_level(RED_GPIO, state);  // light on red
  gpio_set_level(GREEN_GPIO, state);// and green => yellow!!
  vTaskDelay(200 / portTICK_PERIOD_MS); // end loop

Routine de Service d'intérruption (ou ISR, Interrupt Service Routine)

La fonction gpio_isr_register() permet de mettre en place la fonction d'interruption.

mais également, gpio_install_isr_service() et gpio_isr_handler_add().

Interrupt allocation. Un des aspects intéressants à connaître est que vous pouvez conserver les interruptions en IRAM (Instruction RAM, mémoire vive d'instruction) et utiliser les données en DRAM (Data RAM, mémoire vive de données), permettant ainsi moins de latance lors des interruptions, et les gardées ainsi indépendantes des lecture/écriture en flash. À propos de la mémoire dans les ESP32-C3), IRAM et DRAM peuvent être lu/écrit en parallèle.

Exemple sans interruption.

Supprimer l'effet de rebond (Debouncing)

Cet article illustre le problème et 2 méthodes d'anti-rebond avec des exemples sur FPGA, et c'est vraiment bien plus simple à implémenter en FPGA que sur un processeur à but général ou un microcontrôleur.

l'app principale utilise déjà le chronomètre principal vTaskDelay, nous ne pouvons donc pas l'utiliser comme j'ai d'abord fait, dans le cas contraire, il changerait l'adresse de retour de la fonction, et s'arrêterait dans la boucle principale. On pourrait crée un nouvelle xApp, mais la méthode la plus élégante, consiste à utiliser les chronomètre ESP (ESP Timer), cela résoud tous nos problèmes de manière facile.

Chronomètre ESP

Nous allons suivre la seconde méthode et utiliser un chronomètre haute résolition (High Resolution Timer) pour cela. ESP-IDF inclus un exemple dans examples/system/esp_timer/. Nous n'avons pas besoin de nous préoccuper de la très haute résolution de l'horloge, des ms sont suffisant ici, mais, cela à l'avantage de gérer le chronomètre par interruption et d'éviter facilement les conflits d'appels, comme nous en avons absolument besoin en raison des rebonds.

Nous avons besoin de crée la structure à envoyer à la fonction esp_timer_create(), qui initialize l'interruption, et donc, l'entête de la fonction de rappel doit être prédéfinie (ligne 34):

// button manager with debounce timer definition
static esp_timer_handle_t debounce_timer;
static void test_button(void* arg);
const esp_timer_create_args_t debounce_timer_args = {
  .callback = &test_button,
  /* argument specified here will be passed to timer callback function */
  .arg = NULL,
  .name = "test_button"

La fonction chronomètre, est appelée par une interruption à la fin du temps d'exécution de ce chronomètre. Il vérifie alors si le bouton est encore pressé, et change alors l'état (1-0=1, 1-0=1). La LED externe est réglée à l'opposé de l'état courant. Si la LED de la carte clignote, alors la LED externe rouge est éteinte (line 44).

static void test_button(void* arg) {
  if (gpio_get_level(EXTERN_BUT) == 1)
    state = 1 - state;
    gpio_set_level(EXTERN_LED, 1 - state);

La fonction de rappel isr_button_pressed() est appélée lorsque le bouton est pressé. (au signal montant, POSEDGE). En raison du rebond, elle peut être appelée plusieurs fois pour une pression humaine analogique unique. Nous avons donc besoin de vérifier en double que nous somme vraiement dans l'état préssé (tension haute) à la fin d'un délai court, et sinon,de sortir. On vérifie alors que le chronomètre n'avait pas été démarré auparavant. Si il ne l'était pas (!) le cas, alors on lance le chronomètre qui démarre la fonction test_button() après 1 milliseconde (1000 microsecondes). Cela permet d'éviter le rebond, mais de toujours permettre des pression répétées rapides (pour la saisie d'un code morse rapide, où les jeux d'actions furieux) (ligne 52):

static void isr_button_pressed(void* args) {
  if (gpio_get_level(EXTERN_BUT) == 0) 
    return; // we only want to manage pushed button
  if (!esp_timer_is_active(debounce_timer)) { // start only if the timer isn't active
    ESP_ERROR_CHECK(esp_timer_start_once(debounce_timer, 1000)); // 1ms = 1Kµs

p.s.: J'ai trouvé un article à propos de une autre méthode anti-rebond et également spécifique aux possibilités de RTOS.

Installer le système embarqué POSIX, Apache NuttX sur une carte basée sur un RISC-V ESP32-C3 avec Arch Linux

Table of content

Apache NuttX logo
* Introduction
* System packages
* NuttX sources and tools
* Configuration of devkit project, compilation and flash
* Connexion to NSH via (USB) serial
* The ostest and other basic sets (UPDATE)
* The apps examples (UPDATE)
* SPIflash, SMARTFS and file fsystems (UPDATE)
* NSH scripting (UPDATE)


Apache NuttX is a POSIX embedded system available on a lot of microcontrollers boards and architectures. After seeing some articles from Lup Yuen Lee, installing and working with NuttX on Bouffalo BL602 and BL604 RISC-V microcontroller boards, I discovered it can be installed on one of my boards. So I tried and managed to install it this evening on my recently acquired 3.5€ ESP32-C3S SoC nodeMCU board. ESP32-C3 is a SoC with RISC-V RV32IMC microcontroller, integrated 2.4GHz WiFi and Bluetooth LTE. The board contains a CH340 serial-USB converter, so it can easily be used/flashed/debugged from a computer. I already made a post about installing ESP-IDF tools and flashing examples on this RISC-V board.

For people that already know Espressif SoCs, here is a table of the power usage of some of their ESP models:

SoC        Modem sleep  Light sleep mode  Deep sleep mode
ESP8266          20 mA          2,000 µA            20 µA
ESP32            20 mA            800 µA            20 µA
ESP32-C3         20 mA            130 µA             5 µA

This article explains the procedure to prepare environment, on Arch Linux in November 2021. This is for x86_64, but should work on ARM too, only RISC-V toolchains are missing on ALARM, can be compiled, by using x86_64 versions of PKGBUILD (riscv32-elf-binutils, riscv64-elf-gcc). You can find the pricompiled binaries in my ArchLinux ARM archives including a little text about the order of compilation (binlib, gcc-bootstrap, newlib, gcc (and optionnaly, gcc and newlib again). Direct link to the three most usefull archives:
* riscv32-elf-binutils-2.36.1-2-armv7h.pkg.tar.xz
* riscv64-elf-gcc-11.1.0-1-armv7h.pkg.tar.xz
* riscv64-elf-newlib-4.1.0-1-any.pkg.tar.xz

Latest GIT version is needed In November 2021 for ESP32-C3, some other RISC-V architectures are already in stable releases. This is followed by an example of flashing and connect to the NSH shell, via serial on USB terminal. The dependencies for Debian based Linux on the official page, some parts could be incomplete. Some aspects of the NuttX, POSIX compatible Filesystem. OStest, GPIO and SPIflash included examples are also shortly described.

System packages

General system dependencies for NuttX:

sudo pacman -S --needed base-devel ncurses5-compat-libs gperf pkg-config \
   gmp libmpc mpfr libelf expat picocom uboot-tools util-linux git wget

Just press enter to select all packages on the base-devel packages group.

You also need some AUR packages, I still use obsolete Pacaur that some say is obsolete:

pacaur -S --needed  isl kconfig-frontends genromfs

Sadly for my case, there is currently a conflict between kendryte-toolchain-bin (used for Kendryte K210 RV64 SoC), that depend on isl19, currently conflicting with isl, I uninstalled kendryte-toolchain-bin package, hope it would still work from binary archive with last isl version (0.24 now), didn't managed to compile kendryte-toolchain from sources with it.

Specific RISC-V and ESP32 tools, riscv64 GCC is used to compile for both RV64 and RV32 architectures, but we need RV32 specific version of binutils here.:

sudo pacman -S --needed esptool riscv64-elf-gcc riscv32-elf-binutils

Specific RISC-V and ESP32 tools, AUR part (we don't use it in this article, but OpenOCD (Open On-Chip Debugger) can be useful for debugging):

pacaur -S openocd-esp32

You will need a special trick with GNU compilation toolchain, as the current version of NuttX search for riscv64-unknown-elf-* and Arch Linux call them riscv64-elf-* (without unknown-. I just created symlinks in /usr/bin/. Need root privileges for this (UPDATE: g++ was also needed by some optional examples applications):

sudo bash
for tool in gcc ar ld nm objcopy g++
  if [ ! -e /usr/bin/riscv64-unknown-elf-${tool} ]; then
    ln -s riscv64-elf-${tool} /usr/bin/riscv64-unknown-elf-${tool}

Or a more radical solution, to have a link for all existing riscv64-elf-* tools:

sudo bash
cd /usr/bin/
ls riscv64-elf-* | while read bin
do tool=${bin//riscv64-elf-}
  if [ ! -e riscv64-unknown-elf-${tool} ]
    ln -s ${bin} riscv64-unknown-elf-${tool}

NuttX sources and tools

Choose a directory where you will install the tools. I choose a directory called nuttx here:

mkdir nuttx
cd nuttx

Some binaries are needed to creating the file system. The booting partition and the partition table. You can compile them by yourself, but I here just chosen to download already compiled ones, I would maybe update this post with compilation process:


We will just use bootloader and partition-table binaries here, but there is also mcuboot binary in the repository, not sure I will need it later, but I downloaded it to have everything for working in my archives:


NuttX sources are also needed:

git clone nuttx
git clone apps

Update November 26, version 10.2.0 stable is out this week with ">esp32-c3 available by default, NuttX and NuttX-apps tarballs (list + download link for each version), Warning they are both called nuttx-version.tar.gz (Github is stupid), but they contain respectively incubator-nuttx-nuttx-version and incubator-nuttx-apps-nuttx-version files trees

So you can just download them this way to avoid problems:

wget -O incubator-nuttx-nuttx-10.2.0.tar.gz
wget -O incubator-nuttx-apps-nuttx-10.2.0.tar.gz

And unarchive them this way:

tar xf incubator-nuttx-nuttx-10.2.0.tar.gz
tar xf incubator-nuttx-apps-nuttx-10.2.0.tar.gz
ln -s incubator-nuttx-nuttx-10.2.0 nuttx
ln -s incubator-nuttx-apps-nuttx-10.2.0 apps

So they can be usable the standard way.

Configuration of devkit project, compilation and flash

We have now all necessaries tools to prepare and install a bootable and functional system. We now enter in the nuttx directory:

cd nuttx

This is time to choose a project. You can see a list of existing ones for ESP32-C3 by typing:

./tools/ -L | grep esp32c3

Description of some of them are available in the ESP32 (not C3) specific part of the doc

I choose "usbconsole" that contain NSH shell and allow to easily connect via USB using ttyUSB.

./tools/ -l esp32c3-devkit:usbconsole

Then you can tune some advanced parameters but I didn't used it, looks like ESP32-C3 is generic enough to not have to do anything special depending on board, so can be passed.

make menuconfig

You can quit it by pressing 2 times esc key.

Time to compile it now. We can compile it with only one CPU core of the computer:


Or to compile faster (if you don't have too low memory, else it could be slower), you can can add for example -j4 to use 4 cores or your computer:

make -j4

A long compilation sequence will be printed, ending by the names of the binaries (in colour here). This will be the system to be flashed.

CP: nuttx.hex
CP: nuttx.bin
MKIMAGE: ESP32-C3 binary -c esp32c3 elf2image -fs 4MB -fm dio -ff 40m -o nuttx.bin nuttx v3.2
Generated: nuttx.bin (ESP32-C3 compatible)

For flashing it, you need to first plug your ESP32-C3 based board on your computer, then use flashing command. Parameters needed are chip type, (here an esp32c3), serial port where board is connected to (here ttyUSB0) and transfer rate (921600). Following parameters are the memory address where the binaries will be flashed followed by their names (binary files names are coloured here). So bootloader start at 0, partition table start at 0x8000 and NuttX binary we just compiled start at 0x10000. Warning: bootloader and partition-table need to be flashed only when you install for the first time NuttX, the number of write cycle of a flash is limited so only flash what you need: --chip esp32c3 --port /dev/ttyUSB0 --baud 921600 write_flash \
   0x0 ../bootloader-esp32c3.bin \
   0x8000 ../partition-table-esp32c3.bin \
   0x10000 nuttx.bin

Here is the displayed upload, I coloured (and masked) the MAC address. It can be useful to keep it for later. also colored the flashing of the 3 binaries. v3.2
Serial port /dev/ttyUSB0
Chip is ESP32-C3 (revision 3)
Features: Wi-Fi
Crystal is 40MHz
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Configuring flash size...
Flash will be erased from 0x00000000 to 0x00004fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x00010000 to 0x00030fff...
Compressed 19120 bytes to 11416...
Wrote 19120 bytes (11416 compressed) at 0x00000000 in 0.5 seconds (effective 324.4 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 69...
Wrote 3072 bytes (69 compressed) at 0x00008000 in 0.1 seconds (effective 433.2 kbit/s)...
Hash of data verified.
Compressed 131264 bytes to 51049...
Wrote 131264 bytes (51049 compressed) at 0x00010000 in 1.9 seconds (effective 542.5 kbit/s)...
Hash of data verified.

Hard resetting via RTS pin...

Et voilà! The board is flashed (and restarted). In case of errors here, I wrote few lines about most common problems when flashing microcontrollers or FPGA boards using USB on Linux.

Update: I found a new possible problem. If you are already connected to the board by a terminal, the fuse will fail with the following error:

serial.serialutil.SerialException: device reports readiness to read but returned no data (device disconnected or multiple access on port?)

Just have to quit the terminal application, and restart the previous command, everything should run fine now.

Connexion to NSH via (USB) serial

You can now connect with any serial terminal tool like screen (yes it has this functionality too), gtkterm (with a GUI), or lot of others. I used Picocom as proposed by NuttX documentation.
One of the interesting aspect of NuttX, is that you have an integrated shell called NSH (for NuttShell).

picocom -b 115200 /dev/ttyUSB0

To escape from Picocom you need to type: ctrl-a then ctrl-x. Picocom man page contains all the needed shortcuts.

After connecting to the terminal, just type on enter key to have a NSH prompt, you have an integrated help, with the help command. See the more detailed NSH commands description in the official documentation.

nsh> help
help usage:  help [-v] []

  .         cd        echo      hexdump   mv        set       truncate
  [         cp        exec      kill      printf    sleep     uname
  ?         cmp       exit      ls        ps        source    umount
  basename  dirname   false     mkdir     pwd       test      unset
  break     dd        free      mkrd      rm        time      usleep
  cat       df        help      mount     rmdir     true      xd

Builtin Apps:
  nsh  sh

NuttX is POSIX, so as any UniX environment, every device can be accessed via files. Like on Linux, you have the 2 important directories; /dev containing the devices files, and /proc containing system and devices parameters.

Update: If you are stuck for any reason, you can send a reset signal to the board by pressing two times the F7 function key in the terminal.

nsh> ls
nsh> ls -l /dev
 crw-rw-rw-       0 console
 crw-rw-rw-       0 null
 crw-rw-rw-       0 ttyS0
 crw-rw-rw-       0 zero
nsh> ls -l /proc
 dr--r--r--       0 0/
 dr--r--r--       0 1/
 -r--r--r--       0 meminfo
 dr--r--r--       0 fs/
 dr--r--r--       0 self/
 -r--r--r--       0 uptime
 -r--r--r--       0 version

In the included commands, mkrd allow to create a RAMDISK file system

Update: This was the NuttX version 10.2.0-RC0 c7e604b20b-dirty after cat /proc/version.

The ostest and other basic sets (UPDATE)

The sources of the demo programs can be found in boards/risc-v/esp32c3/esp32c3-devkit/src/ subdirectory and the .config settings, that can also be changed with make menuconfig, for each demo are located in the boards/risc-v/esp32c3/esp32c3-devkit/configs/ directory.

For my second test, I tried esp32c3-devkit:ostest receipe. The system include a command called ostest, that try different systems usage, including memory allocation, timers or multithreading.

If you have already compiled a project and try to compile another one you will have an alert:

Already configured!
Please 'make distclean' and try again.

You only need to follow the instruction and make a distclean to be able to compile another one:

make distclean
[...] <- lot of cleaning
./tools/ -l esp32c3-devkit:ostest

As we already flashed the boot en partition table and number of write cycle of flash are limited (to some thousand of times, look at the flash eprom specification), you can limit the flash to the nuttx.bin part itself: --chip esp32c3 --port /dev/ttyUSB0 --baud 921600 write_flash \
   0x10000 nuttx.bin

You will only be able to see the name of the command by typing help :

nsh> help
help usage:  help [-v] []
[...] <- standard commands
Builtin Apps:
  nsh     ostest  sh

When the ostest finish on this system image, you will see memory used. and the filesystem will have a new directory /var with a subdirectory /var/mqueue added.

nsh> ostest
[...] <= lot of tests
barrier_func: Thread 6 done
barrier_test: Thread 5 completed with result=0
barrier_test: Thread 6 completed with result=0
barrier_test: Thread 7 completed with result=0

End of test memory usage:
======== ======== ========
arena       5d2e0    5d2e0
ordblks         8        8
mxordblk    56710    53ab0
uordblks     5de0     9640
fordblks    57500    53ca0

Final memory usage:
======== ======== ========
arena       5d2e0    5d2e0
ordblks         2        8
mxordblk    58ae0    53ab0
uordblks     47f0     9640
fordblks    58af0    53ca0
user_main: Exiting
ostest_main: Exiting with status 0
nsh> ls var/

As with Linux, you can know the total, used and free memory by displaying the content of /proc/meminfo. The builtin command free do the same thing.

nsh> cat /proc/meminfo
                   total       used       free    largest  nused  nfree
        Umem:     382752       6128     376624     376624     32      1

Among the other tests, some demos that interest me are:
* esp32c3-devkit:gpio to access to GPIO (general purpose Input/Output) via command line.
* esp32c3-devkit:spiflash to access the flash disk using NuttX SMART Flash file system via SPI1. The command mksmartfs can be used to make the FS, and the FS can be mounted the POSIX way (like Linux for example) by typing mount -t smartfs /dev/... /mntpoint. It add mksmartfs command and flash_eraseall and fstest builtin apps.
* esp32c3-devkit:lvgl demo, LVGL is a small graphic and GUI library that help to make interfaces in small memory embedded board systems. Demos include drivers for Sitronix ST7735 (262K Color Single-Chip TFT Controller/Driver) and ST7789 SPI displays controllers in esp32c3_st7735.c and esp32c3_st7789.c in boards/risc-v/esp32c3/esp32c3-devkit/src/. I need to figure out how to connect one. Breadboards will help for this test. Another solution is probably to try it using Qemu?? With this demo the RGB led of the board is set to orange. There is a Youtube video of this demo on an ESP32 (Xtensa) version monothread and multithread, side by side. The ESP32-C3 has only one core. The command in the system is called lvgldemo. Nothing appear on terminal console, so I don't know at all what this demo do. There are some documentation about NuttX and LVGL on the LVGL documentation site. There is also tutorial site.

The apps examples (UPDATE)

We previously downloaded the git apps repository beside the nuttx repository. The apps, contains examples beyond the basic vital functionalities of the system. These resources can be set in the .config file (flags starting with CONFIG_EXAMPLES_) or by using make menuconfig.

In the menu config the apps are in the last entry. Go down with cursor keys and when you are on the last entry, just press enter:

Then the application configuration submenu will display. Warning, you have generally limited resources on the board, select options with parsimony. The best is to first choose the already prepared set for your platform and then to activate some if they work:
* Cryptographic Library Support contains LibTomCrypt and Mbed TLS Cryptography libraries support.
* The Examples subset contains interesting tools like, audio generator (need external dependencies), uIP web server (implementation on uIP TCP/IP stack, that include a DHCP client), tools to blink LEDs (warning PowerLED needs external dependencies), battery monitor, camera driver, the LVGdemo, pdcurses (a GUI text lib).
* The filesystem utilities contains mkgpt, mkbr (for managing fs), a password file support (need external dependencies) etc... :
* The network utilities contains, a chat tool, a sJSON and CODEC libraries, an FTP client, MQTT-C (a MQTT messages client), and a remote execution server and client.
* The NSH library allow you to tune NSH parameters, including MOTD management, a more heavy and complete Command Line Editor, instead of the minimal readline(), or backslash of characters.
* The System Libraries of NSH add-ons, allow to enable system() interface (and so call of NSH function from C), Terminal Curses control support an hexadecimal file editor, or a minimal CU terminal editor. There are also options to use Emacs, VIM or Ncurses mode interface. Zmodem and GPIO u-Block modem., SPI tools, etc...
* Wireless Libraries and NSH Add-ons, contains Bluetoot and IEEE 802.15.4 applications including both version of "Swiss army knife".


The esp32c3-devkit:gpio add a command to access to GPIO (general purpose Input/Output) via command line. It can be set in make menuconfig by Application configuration ---> Examples ---> [*] GPIO driver example, or by setting the .config file.

To have GPIO activated:

# IO Expander/GPIO Support

To have the command example:


With this a new gpio command appear, if you type it without arguments, it will help you (-h argument works too):

nsh> gpio
ERROR: Missing required arguments
USAGE: gpio [-w ] [-o ] 
       gpio -h
	: The full path to the GPIO pin driver.
	-w : Wait for an signal if this is an interrupt pin.
	-o :  Write this value (0 or 1) if this is an output pin.
	-h: Print this usage information and exit.

You can list the available devices (driver path in the help). Here is a long (-l) listing, and you can see they are character devices files with the initial c:

nsh> ls -l /dev
 crw-rw-rw-       0 console
 crw-rw-rw-       0 gpint0
 crw-rw-rw-       0 gpout0
 crw-rw-rw-       0 gpout1
 crw-rw-rw-       0 null
 crw-rw-rw-       0 ttyS0
 crw-rw-rw-       0 zero

In the example source boards/risc-v/esp32c3/esp32c3-devkit/src/esp32c3_gpio.c, we can see than the pins 1 and 2 are set to the two first gpout, here gpout0 and gpout1 and that only the pin 9 is used for the interrupts gpint0:

/* Pin 1 and 2 are used for this example as GPIO outputs. */

#define GPIO_OUT1  1
#define GPIO_OUT2  2
/* Interrupt pins.  GPIO9 is used as an example, any other inputs could be
 * used.

#define GPIO_IRQPIN  9

Here is a map of the pins on my board, thanks to J-C. François, licence CC-BY-SA:

The pins 1 and 2 are at the upper left, and the pin 9 at the upper right

The current state of a GPIO can be know by a cat. here for example 0 state (the result is without return carriage so just touch by nsh:

nsh> cat /dev/gpout0

Writing a value 1 to the first pin defined (so pin 1) the example display the current value, then change it and verify its state:

nsh> >gpio -o 1 /dev/gpout0
Driver: /dev/gpout0
  Output pin:    Value=0
  Writing:       Value=1
  Verify:        Value=1

SPIflash, SMARTFS and file systems (UPDATE)

The SPIflash recipe ( esp32c3-devkit:spiflash ), add the commands mksmartfs and the builtin apps flash_eraseall and fstest.

To use fstest, you need to prepare the fs and mount it.

In the default included commands, mkrd allow to create a RAMDISK file system. It can be used for tests and avoid to waste flash writing cycles.

The syntax is (help mkrd to display it):

mkrd usage:  mkrd [-m ] [-s ] 

Default secteur size is 512 bytes. We can simply create a 10KB RAM disk, so 512×20 = 10240 bytes = 10 kilobytes bytes, by typing:

mkrd 20

A ramdisk device will appear in /dev called ram0. Filesystems are blocks devices (shown by the initial b, c is character device):

nsh> ls -l /dev
 crw-rw-rw-       0 console
 crw-rw-rw-       0 null
 brw-rw-rw-   10240 ram0
 brw-rw-rw-  983040 smart0
 crw-rw-rw-       0 ttyS0
 crw-rw-rw-       0 zero

You can also see the smart0 device corresponding to the disk, is also available with the SPIflash recipe. sadly RAM disk isn't supported by smartFS so it can't be formatted as is. VFat is supported, but need to add it at configure time. You can activate TMPFS (see below) that also use RAM, as on Linux, for working on temporary RAM disk.

* SmartFS support can be activated by make menuconfig in File Systems ---> -*- SMART file system
* smartfs application can be selected by make menuconfig in Application Configuration ---> File System Utilities ---> mksmartfs.

Flags set in .config, for SMARTFS support:


And for command utilities:


smart0 need to be formated before mounting it. The data will be kept on it after reboot, or flashing a new code.

nsh> mksmartfs /dev/smart0

In POSIX systems, we need to create an empty directory used as a mount point, then mount the partition. The type of filesystem isn't auto-detected, so we need to pass it to mount command here:

mkdir /mnt
mount -t smartfs /dev/smart0 /mnt

It will make a test loop of 100 iterations, of writing and then deleting files in the mountpoint, I put the ouput. If you really want to test the fs you can use it, but you will waste write cycles of the flash :

The commande is:


Here is a sample of the output, I stopped it by resetting the card (2 times F7:

=== FILLING 4 =============================
168. Type[8]: File  Name: iJaUkEIwgSG4cAyl8J
169. Type[8]: File  Name: FNLd0XaTSYF8TV3YtO
170. Type[8]: File  Name: SV0Lrtmigq9Yg4SauoD4f
171. Type[8]: File  Name: GJfbQ0bFaowO0ep
Total file size: 427496

=== DELETING 4 ============================
Deleted some files
  Number of files: 215
  Number deleted:  108
 1. Type[8]: File  Name: PRMIRHz9ZwgHga
 2. Type[8]: File  Name: 9LH4Uf67RL4bFXVCykg3
Total file size: 427496
File System:
  Block Size:      1024
  No. Blocks:      960
  Free Blocks:     457
  Avail. Blocks:   457
  No. File Nodes:  0
  Free File Nodes: 457

End of loop memory usage:
======== ======== ========
arena       58fb0    58fb0
ordblks        41       57
mxordblk    510a0    510a0
uordblks     77d0     6f40
fordblks    517e0    52070[...]

As I stopped it during the loop the files are still on the fs. Wildcard are not supported by the rm command.

As we can see with the df (disk free) command, after deleting some file, this still uses lot of blocks:

nsh> df
  Block    Number
  Size     Blocks       Used   Available Mounted on
  1024        960        725         235 /mnt
     0          0          0           0 /proc

I used the flash_eraseall builtin app to erase evrything:

nsh> flash_eraseall /dev/smart0

This remove the files but don't free the blocks. I needed to force a mksmartfs with the option -f to free the blocks:

It removed the files but didn't cleaned the block. I need to format again the device using:

nsh> mksmartfs -f /dev/smart0

Now All the blocks are available again:

nsh> df
  Block    Number
  Size     Blocks       Used   Available Mounted on
  1024        960         10         950 /mnt
     0          0          0           0 /proc

For example, write a single file to be kept after reboot. we can write the content of the file with cat or echo commands for example. I write here a simple sentence this is kept datas in the test.txt file, using the file redirection character (>):

nsh> echo "this is kept datas" >/mnt/test.txt
nsh> df
  Block    Number
  Size     Blocks       Used   Available Mounted on
  1024        960         11         949 /mnt
     0          0          0           0 /proc

As you can see this took an entire 512 bytes block even if it's an only 18 bytes string file. Every file written use a full block, so try to choose the sector-size when you use mksmartfs.

After rebooting the device (2 times F7) or unplug/replug it. You can still see the data on the file. Note that after each reboot, you need to mount afain the filesystem:

nsh> mount -t smartfs /dev/smart0 /mnt
nsh> cat /mnt/test.txt
this is kept datas

Removing files by rm command effectively free the blocks:

nsh> rm test.txt
nsh> df
  Block    Number
  Size     Blocks       Used   Available Mounted on
  1024        960         10         950 /mnt

Among the other available file systems in NuttX, there is LittleFS, CNX-Software made an article about it. It is very compact and reliable, but there is no utilies to manage them in applications, need to use libs or code a shell compatible command. The Source Git repository shows an exemple usage in C

By make menuconfig select Board Selection ---> [*] Mount SPI Flash MTD on bring-up (LittleFS) ---> (X) LittleFS (Only one file system can be set at a time here), and

In the .config, if the file sustem is not set for the board:



For the more general LITTLEFS support, by make menuconfig, in File Systems ---> -*- LITTLEFS File System (several can be selected in this part).

Options in .config:


With this setting (after selecting SPIflash), there is a esp32c3flash device that replace smart0.

And The smartFS can't be mounted. The error message is a bit erroneous:

nsh> mount -t smartfs /dev/esp32c3flash /mnt
nsh: mount: mount failed: No such device


If you choose in make menuconfig the option File Systems -> [*] TMPFS file system

Default TMPFS option in .config


You will see an automounted /tmp, it is 512 blocks wide on my board (so 512×512/1024=512/2=256KB of RAM disk. 1 block is used for the file system root directory (mounted in /tmp):

nsh> df
  Block    Number
  Size     Blocks       Used   Available Mounted on
     0          0          0           0 /proc
   512          1          1           0 /tmp

TMPfs allow to use the free memory, but don't really use it until your write files inside:

nsh> cat /proc/meminfo
                   total       used       free    largest  nused  nfree
        Umem:     364464      10016

354448 354448 38 1

I cd (change directory) in /tmp here and write a file, it will take one more 512 bytes block and so, 10016 + 512 (block) + 176 (probably some references in filesystem dictionnary) = 10704 bytes of memory will be used. The size of the refs vary it took only 96 bytes on another test.

nsh> cd /tmp
nsh> echo "This is a test" >test
nsh> df
  Block    Number
  Size     Blocks       Used   Available Mounted on
     0          0          0           0 /proc
   512          2          0           2 /tmp
nsh> cat /proc/meminfo
                   total       used       free    largest  nused  nfree
        Umem:     364464      10704     353760     353744     42      2

NSH scripting (UPDATE)

You can make simple shell script in NSH. For exemple this one lie exemple test if /dev/ram0 exists and then print the result of the test.

if [ -e /dev/ram0 ]; then echo "ram0 exists"; else echo "no ram0"; fi

Variable can be set and unset

nsh> set foo bar
nsh> echo $foo

script can be put in files. I didn't found an easy way to put them. The cat function, seems to be limited to go from one file to another, it doesn't manage STDIN a standard way, and there is no editor. The only solution is to echo line by line, or to transfer files by network or serial (need to search more).
If you have activated the TMPFS as explained previously or formatted and mounted SMARTFS partition, you can write files inside. Here is an exempla with TMPFS, available in /tmp by default I removed the initial nsh> here, so it will be easier to copy paste:

echo if [ -e /dev/ram0 ] >/tmp/ 
echo then >>/tmp/
echo "  echo ram0 exists" >>/tmp/
echo else >>/tmp/
echo "  echo ram0 doesn't exist" >>/tmp/
echo fi >>/tmp/

The >> symbol means you append lines to an existing file instead of overwriting it.
you can now see the script by a cat:

nsh> cat /tmp/
if [ -e /dev/ram0 ]
  echo ram0 exists
  echo ram0 doesn't exist

And it can be executed by two means, sh or source, the difference,

nsh> sh /tmp/
ram0 doesn't exist

Now, if we create ram0 by using mkrd:

nsh> mkrd 20
nsh> sh /tmp/
ram0 exists

Using ESP-IDF with RISC-V ESP32-C3 nodeMCU module on a Linux system

>NodeMCU ESP32-C3S »></a></p>
<h2 id=Table of Content

* Introduction
* Step 1, install dependencies
* Step 2, install ESP-IDF
* Step 3, setup working environment
* Step 4, test « Hello World » example
* Step 5, tune the target, if needed
* Step 6, flash the project
* Step 7, viewing the output
* Troubleshooting
** Invalid value for ‘{esp32|esp32s2}’: ‘esp32c3′ is not one of ‘esp32′, ‘esp32s2′
** libsodium/crypto_box/curve25519xchacha20poly1305/box_seal_curve25519xchacha20poly1305.c
** Flashing errors
*** Detection, power, and device that disappear
*** usbfs: interface 0 claimed by ch341 while ‘brltty’ sets config #1
** Problem with CA certificates bundle


The list of existing ESP32-C3 SoC modules (with their specs) is very interesting, This one RISC-V core SoC has lower computing power than the ESP32-S3 (dual-core Xtensa ISA based, with a RISC-V co-processor core for « ultra low power » (ULP) mode), but RISC-V architecture is also far more efficient and will for sure have longer lifetime support and evolution due to its openness. ESP32-C3 use an RV32IMC (Integer, Multiplication/division and Compressed extensions) core.

I bought for my test a ESP-C3-32S-Kit on AliExpress (for ~3.4€). There is also an interesting option, where you can use really tiny 3~4 € ESP-C3-01M-Kit programmer interface/mother board, with a SoC included alone on a daughter (ESP32-C3M with an on PCB antenna). This allows, to flash several pads one only one programmer board. This reduce size of the needed system for final application to just the SoC and it’s power supply and peripherals. Pads of the SoC daughter board are wide enough to be easily soldered.

ESP-IDF is a tool to develop in C language on Espressif platforms, including ESP8266 and ESP32 series. It uses FreeRTOS free and open source embedded real-time OS for its SoCs. Sadly the official documentation for installing it is incomplete with the current state (November 2021) of GIT repository. Here is the mean to install a working ESP-IDF for this SoC series on Linux. This will be more compact and straight forward that the official with Linux+Windows installation.

About RISC-V more generally, I created this week a list of available Open Source RISC-V implementations, and open sources tools to build them or work with them.

Step 1, install dependencies

Instruction for Arch Linux, CentOS and Debian, based Linux systems:

* Debian/Ubuntu/…

sudo apt-get install git wget flex bison gperf python3 python3-pip python3-setuptools cmake \
  ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0

* CentOS

sudo yum -y update && sudo yum install git wget flex bison gperf python3 python3-pip python3-setuptools \
  cmake ninja-build ccache dfu-util libusbx

* Arch Linux/Manjaro/…

sudo pacman -S --needed gcc git make flex bison gperf python-pip cmake ninja ccache dfu-util libusb

Step 2, install ESP-IDF

This will take at least 1.2GB of disk space (size of the git clone at the time I write this article).

mkdir -p ~/esp
cd ~/esp
git clone --recursive

They didn’t mention, that 4.3 is required to use ESP32-C3. At the time I tried it, version 4.2 is currently (November 2021) the default branch, you can see the current version by this commands. Warning: This start the environment, so you will probably need to log again or simply start another shell to avoid problems when finishing the installation.:

cd ~/esp/esp-idf
. ./ --version

So you need to checkout the 4.3 branch to have a working version with ESP32-C3, and then update submodule to match the new version:

cd ~/esp/esp-idf
git checkout release/v4.3
git submodule update --init --recursive

Then you need to install some binaries/libs. Warning, it will create a ~/.expressif directory to place all downloaded files inside, don’t forget to create the directory + a link to a partition with enough place (about 1GB needed for all versions).

cd ~/esp/esp-idf
./ esp32c3

After the installation, the following command is suggested to enter in the working (python pyenv) environment (don’t forget the first . and space)

Step 3, setup working environment

. ./

A message will be displayed that explain to you how to compile a program:

Done! You can now compile ESP-IDF projects.
Go to the project directory and run: build

They suggest in the documentation to add an alias to your .bashrc:

alias get_idf='. $HOME/esp/esp-idf/'

So you will only have to type get_idf to get ready to use it.

Step 4, test « Hello World » example

I would personally make it this way:

cd ~/esp/
mkdir tests
cd tests
cp -a ~/esp/esp-idf/examples/get-started/hello_world .
cd hello_world set-target esp32c3

Errors possible, see:
* Invalid value for ‘{esp32|esp32s2}’: ‘esp32c3′ is not one of ‘esp32′, ‘esp32s2′
* libsodium/crypto_box/curve25519xchacha20poly1305/box_seal_curve25519xchacha20poly1305.c

Step 5, tune the target, if needed

You need now to use menuconfig if you want to setup some parameters, default one should just work fine. menuconfig

This show you an interactive cli menu interface like for the Linux kernel, that allow you to tune some parameters. You don’t need to tune them for this test.

You can navigate with cursor keys, validate or enter submenus with enter, and quit them without modification with esc key. When you quit it, it will say you if you didn’t done any modification:

No changes to save (for '~/esp/test/hello_world/sdkconfig')

Step 5 build the project

Now as everything is setup, you only need to build the project, this could take some time, as it, at first time, compile, the whole embedded system: build

It will end with a message displaying the location of the binary to be flashed and the instruction to type to flash it, the long explicit way and the short implicit way (I coloured them here):

Generated >/data/arc/esp/test/hello_world/build/hello-world.bin

Project build complete. To flash, run this command:
/home/popolon/.espressif/python_env/idf4.3_py3.9_env/bin/python ../../esp-idf/components/esptool_py/esptool/ -p (PORT) -b 460800 --before default_reset --after hard_reset --chip esp32c3  write_flash --flash_mode dio --flash_size detect --flash_freq 80m 0x0 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/hello-world.bin
or run ' -p (PORT) flash'

* Problem with CA certificates

Step 6, flash the project

If you didn’t have plugged your board in the USB port it’s the time to do. Then (by default white colour RGB led is enlighten. Type then the suggested command. I choose the short way here: --port /dev/ttyUSB0 flash

It should now do the flashing sequence. I show only some important steps. In color you MAC address is given, it can be useful to detect if everything run fine.

Serial port /dev/ttyUSB0
Chip is ESP32-C3 (revision 3)
Features: Wi-Fi
Crystal is 40MHz
Uploading stub...
[...] <- lot of output
Writing at 0x00010000... (20 %)
[...] <- lot of output
Hard resetting via RTS pin...

Read this if you have some troubles at flashing.

Step 7, viewing the output

You can connect to the board, still with its ttyUSB0 port, need to connect at speed 115200. I previously wrote an article about how to control a remote board, including serial

I use TinySerial here:

com /dev/ttyUSB0 115200

You can also use to monitor the output like this ( monitor shortcuts and launching GDB with GDBStub: -p /dev/ttyUSB0 monitor

In both case, you will see a booting sequence, an Hello World! some characteristics of your board, then a 10 seconds counter and a reboot. This is looping until you unplug or flash again the board.

Hello world!
This is esp32c3 chip with 1 CPU core(s), WiFi/BLE, silicon revision 3, 2MB external flash
Minimum free heap size: 327588 bytes
Restarting in 10 seconds...
Restarting in 9 seconds...
Restarting in 8 seconds...

You can quit by typing: the 2 keys ctrl-c followed then by a

Building, flashing and monitor can be combined by one command: -p /dev/ttyUSB0 flash monitor


I don’t want to pollute too much the reading of the workflow and keep it as reference, so people can found answer when they encouter the same errors.

Invalid value for ‘{esp32|esp32s2}’: ‘esp32c3′ is not one of ‘esp32′, ‘esp32s2′

If you have the following error, you didn’t checkout the 4.3 branch at Step 2

Usage: /data/arc/esp/esp-idf/tools/ set-target [OPTIONS] {esp32|esp32s2}
Try '/data/arc/esp/esp-idf/tools/ set-target --help' for help.

Error: Invalid value for '{esp32|esp32s2}': 'esp32c3' is not one of 'esp32', 'esp32s2'.


If during the set-target you have the following error:

CMake Error at ~/esp/esp-idf/tools/cmake/component.cmake:475 (add_library):
  Cannot find source file:


This mean you didn’t made the git submodules update in Step 2. So return in ~/esp/esp-idf and git submodule update.

You will then need to remove the hello_world directory and copy it again in tests: cd ~/esp/tests/; rm -R hello_word; cp -a ~/esp/esp-idf/examples/get-started/hello_world ..

Flashing errors

There are several problems possible for flashing part, most of them are about ttyUSB0 detection/usage.

Detection, power, and device that disappear


First look if your board is detected by lsusb.

* If the device isn’t detected at all, verify if you USB wire is not a powering only wire. Try by connecting other USB devices, if all of them aren’t detected by the wire, this is probably the case, if you use a wire for your phone with the same kind of connector, try if it detected with it, then your board.

* Another case, a board that worked perfectly the first day, was not detected any more the second day. I looked at it with artisanal microscope using phone zoom + cheap lens, and seen a hair or wire, like on the detail picture at left, just above two pins of the USB connector. After removing it, the board work again.

* If the interface is displayed by lsusb but disappear when flashing it, try to disable power saving (autosuspend) for this device or all USB devices, lots of old mother boards have problems with USB devices power saving. Some mouses keep stuck, some flashable devices disappear, etc…

usbfs: interface 0 claimed by ch341 while ‘brltty’ sets config #1

If it fails with a long error message including around the 10th line:

FileNotFoundError: [Errno 2] No such file or directory: '/dev/ttyUSB0'

Then there is a problem with the USB detection in your system configuration.

You can dig in kernel message with dmesg | less then search for ttyUSB0 (just type /ttyUSB0 to found the problem.

In my case for example, the brtty (tty for braille) driver tried to get the serial device and then disconnected it:

[36767.028312] usb 1-1.6: ch341-uart converter now attached to ttyUSB0
[36767.252867] audit: type=1130 audit(1637441179.890:468): pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=brltty-device@sys-devices-pci0000:00-0000:00:1a.0-usb1-1\x2d1-1\x2d1.6 comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[36767.878792] audit: type=1130 audit(1637441180.513:469): pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=brltty@-sys-devices-pci0000:00-0000:00:1a.0-usb1-1\x2d1-1\x2d1.6 comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[36767.883002] usb 1-1.6: usbfs: interface 0 claimed by ch341 while 'brltty' sets config #1
[36767.883975] ch341-uart ttyUSB0: ch341-uart converter now disconnected from ttyUSB0
[36767.883998] ch341 1-1.6:1.0: device disconnected

You can in this case not the device id with the following command (the ID in the result is coloured here:

lsusb | grep CH340
Bus 001 Device 005: ID 1a86:7523 QinHeng Electronics CH340 serial converter

Then edit the 90-brltty-device.rules:

>code>sudo vi /usr/lib/udev/rules.d/90-brltty-device.rules

Search the line containing 1a86/7523 by typing /1a86/7523, then comment the line by adding a # at start:

#ENV{PRODUCT}=="1a86/7523/*", ENV{BRLTTY_BRAILLE_DRIVER}="bm", GOTO="brltty_usb_run"

save and quit by typing :x if you use vi/vim/nvim… and then reload udev rules by typing:

systemctl reload systemd-udevd

It should now be ok, by just unplugging and replugging the USB wire from your computer.

Generally all the kind of (tty)USB connection problem can be solved by tuning /etc/udev/rules.d/

Problem with CA certificates bundle

With the 4.2 version of ESP-IDF, some FAILED are thrown in relation with a x509_crt_bundle and cacrt_all.pem invalid certificate.

Updating the Certificate Bundle section has a link to himself as solution. ^^. Looks like the problem is on ESP-IF 4.2, 4.3 runs just fine, see install + upgrade process.

My system CA-certificates are already up to date but it want a specific version to put somewhere (not documented) to be able to push it on the board firmware. Ca-certificates, are certificates about CA (Certificate Authorities) that you need to be sure that this authorities are still be considered as valid. anyway ESP-IF 4.3 works just fine.