Restart repo
This commit is contained in:
24
esp32/index.rst
Normal file
24
esp32/index.rst
Normal file
@@ -0,0 +1,24 @@
|
||||
..
|
||||
Copyright (C) 2023 Jeremie Salvi.
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||
A copy of the license is included in the section entitled "GNU
|
||||
Free Documentation License".
|
||||
|
||||
Programmation d'un ESP32
|
||||
========================
|
||||
|
||||
Ici je vais poser toutes mes astuces pour la programmation et le débugage d'un ESP32,
|
||||
ainsi qu'une présentation de mes projets. Je tiens à noter que je suis passé sous
|
||||
ArchLinux, l'installation et la configuration va donc légèrement changer par rapport à Debian.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
installation.rst
|
||||
vscodium_integration.rst
|
||||
qemu_emulation.rst
|
||||
qemu_ethernet.rst
|
||||
163
esp32/installation.rst
Normal file
163
esp32/installation.rst
Normal file
@@ -0,0 +1,163 @@
|
||||
..
|
||||
Copyright (C) 2023 Jeremie Salvi.
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||
A copy of the license is included in the section entitled "GNU
|
||||
Free Documentation License".
|
||||
|
||||
Installation de l'environnement sous ArchLinux
|
||||
==============================================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Tout d'abord je tiens à vous rassurer, cela doit fonctionner sur la majorité des distributions.
|
||||
Je vais utiliser vscodium (fork de visual studio code sans la télémétrie microsoft),
|
||||
un framework basé sur electron, donc compatible multi OS et multi distributions.
|
||||
L'emulateur basé sur QEMU sera compilé à la main, donc ça devrait passer sur n'importe quel linux 😊.
|
||||
|
||||
.. warning:: Je suis passé en root pour ne pas m'encombrer avec ``sudo``, pensez y si nécessaire
|
||||
|
||||
Pour plus d'infos en général, allez faire un tour sur `La documentation officielle de l'esp 32 <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/>`_
|
||||
|
||||
Installation de vscodium
|
||||
------------------------
|
||||
|
||||
Vscodium n'est pas disponible dans les paquets officiels ArchLinux, il faut donc aller sur les ArchLinux User Repositories (AUR.)
|
||||
|
||||
`Dépot AUR des binaires vscodium <https://aur.archlinux.org/packages/vscodium-bin>`_
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
:caption: installation de vscodium
|
||||
|
||||
cd /opt
|
||||
git clone https://aur.archlinux.org/vscodium-bin.git
|
||||
cd vscodium-bin/
|
||||
makepkg
|
||||
pacman -U vscodium-bin-1.72.2.22289-1-x86_64.pkg.tar.zst
|
||||
|
||||
.. warning:: Si vous êtes en root pour programmer, il faut utiliser les arguments suivants pour
|
||||
lancer vscodium : ``codium --no-sandbox --user-data-dir ~/.config/vscodium``
|
||||
|
||||
Pour le confort de développement, nous allons installer une extension pour améliorer
|
||||
l'autocompletion en C/C++. Elle n'est pas disponible sur le marketplace vscodium, il faut l'installer
|
||||
manuellement. On la télécharge `ici <https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools>`_.
|
||||
|
||||
On l'installe depuis le menu des extensions comme ci dessous.
|
||||
|
||||
.. image:: ../images/esp32/ms_cpp_extension.png
|
||||
:width: 400
|
||||
|
||||
Nous allons aussi utiliser l'extension native debug pour l'intégration du debugeur.
|
||||
Elle se trouve dans le store. Pour l'utilistation, on verra ça dans la partie
|
||||
`création d'un environnement vscodium </esp32/vscode_integration.html#configurer-le-debugeur>`_.
|
||||
|
||||
.. image:: ../images/esp32/native_debug_extension.png
|
||||
:width: 400
|
||||
|
||||
Installation des prérequis pour l'esp-idf (Espressif IoT Development Framework)
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
La commande est directement fournie dans la documentation, il suffit de la copier coller :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pacman -S --needed gcc git make flex bison gperf python cmake ninja dfu-util ccache libusb
|
||||
|
||||
Installation et configuration de l'esp-idf
|
||||
------------------------------------------
|
||||
|
||||
Dans un premier temps, on récupère les dépots :
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
|
||||
mkdir -p /opt/esp
|
||||
cd /opt/esp
|
||||
git clone --recursive https://github.com/espressif/esp-idf.git
|
||||
|
||||
Il faut ensuite installer les outils. Par defaut ils sont installés dans ``$HOME/.espressif``.
|
||||
On peut changer cet emplacement grâce à la variable d'environnement ``IDF_TOOLS_PATH``.
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
|
||||
export IDF_TOOLS_PATH=/opt/esp/esp-idf/.espressif
|
||||
cd esp-idf
|
||||
./install.sh esp32
|
||||
|
||||
L'environnement utilise un paquets d'outils, il est dur de mémoriser tout
|
||||
les chemins et les variables d'environnement. Heureusement on nous fournit aussi un sctipt
|
||||
qui rend tout ça accessible depuis la console. Pour ça il faut le charger :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
. /opt/esp/esp-idf/export.sh
|
||||
|
||||
Comme il faut l'appeller dans chaque nouveau terminal, on
|
||||
se créer un alias pour faciliter l'accès :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
alias esp_idf_import=". /opt/esp/esp-idf/export.sh"
|
||||
|
||||
On colle cette ligne dans notre .bashrc ou notre .profile. Pour vérifier qu'on
|
||||
a bien tout importé, on tappe la commande env, et on devrait avoir les infos
|
||||
suivantes :
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
|
||||
env
|
||||
>> ESP_IDF_VERSION="5.1"
|
||||
>> ESP_ROM_ELF_DIR="/opt/esp/esp-idf/.espressif/tools/esp-rom-elfs/20220823/"
|
||||
>> IDF_DEACTIVATE_FILE_PATH="/tmp/tmpxj9n6nndidf_46523"
|
||||
>> IDF_PATH="/opt/esp/esp-idf"
|
||||
>> IDF_PYTHON_ENV_PATH="/opt/esp/esp-idf/.espressif/python_env/idf5.1_py3.10_env"
|
||||
>> IDF_TOOLS_EXPORT_CMD="/opt/esp/esp-idf/export.sh"
|
||||
>> IDF_TOOLS_INSTALL_CMD="/opt/esp/esp-idf/install.sh"
|
||||
>> IDF_TOOLS_PATH="/opt/esp/esp-idf/.espressif"
|
||||
>> OPENOCD_SCRIPTS="/opt/esp/esp-idf/.espressif/tools/openocd-esp32/v0.11.0-esp32-20221026/openocd-esp32/share/openocd/scripts"
|
||||
>> PATH="/opt/esp/esp-idf/components/esptool_py/esptool:/opt/esp/esp-idf/components/espcoredump:/opt/esp/esp-idf/components/partition_table:/opt/esp/esp-idf/components/app_update:/opt/esp/esp-idf/.espressif/tools/xtensa-esp-elf-gdb/12.1_20221002/xtensa-esp-elf-gdb/bin:/opt/esp/esp-idf/.espressif/tools/xtensa-esp32-elf/esp-2022r1-11.2.0/xtensa-esp32-elf/bin:/opt/esp/esp-idf/.espressif/tools/esp32ulp-elf/2.35_20220830/esp32ulp-elf/bin:/opt/esp/esp-idf/.espressif/tools/openocd-esp32/v0.11.0-esp32-20221026/openocd-esp32/bin:/opt/esp/esp-idf/.espressif/python_env/idf5.1_py3.10_env/bin:/opt/esp/esp-idf/tools:/usr/local/sbin:/usr/local/bin:/usr/bin"
|
||||
|
||||
Si l'on à pas les chemins ``/opt/esp/esp-idf`` dans le path et les variables d'environnement ``IDF_``, les outils fournis
|
||||
dans l'ESP-IDF ne fonctionneront tout simplement pas.
|
||||
|
||||
Compilation de qemu-system-xtensa patché pour les esp32
|
||||
-------------------------------------------------------
|
||||
|
||||
Espressif, l'entreprise qui concoit les ESP32, nous fournit un fork de QEMU modifié pour emmuler celui ci.
|
||||
On le trouve à l'addresse suivante : `GitHub du fork QEMU <https://github.com/espressif/qemu>`_.
|
||||
Toute la procédure est expliquée ici : `Wiki du dépot <https://github.com/espressif/qemu>`_.
|
||||
|
||||
Je ne vais pas tout réexpliquer ici, ce qu'il faut savoir c'est que nous ne voulons pas
|
||||
recompiler tout qemu, juste le binaire qemu-system-xtensa modifié pour esp32. Je vais donc
|
||||
refaire la compilation en désactivant un maximum de fonctionnalités inutiles. On trouve la
|
||||
liste des fonctionnalités dans le fichier ``configure`` du dépot.
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
:caption: compilation de QEMU pour esp32
|
||||
|
||||
#!/bin/bash
|
||||
cd /opt
|
||||
git clone https://github.com/espressif/qemu.git
|
||||
cd qemu
|
||||
./configure --target-list=xtensa-softmmu \
|
||||
--enable-gcrypt \
|
||||
--enable-debug --enable-sanitizers \
|
||||
--disable-strip --disable-user \
|
||||
--disable-capstone --disable-vnc \
|
||||
--disable-sdl --disable-gtk \
|
||||
--disable-opengl --disable-vfio-user-server
|
||||
ninja -C build
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
307
esp32/qemu_emulation.rst
Normal file
307
esp32/qemu_emulation.rst
Normal file
@@ -0,0 +1,307 @@
|
||||
..
|
||||
Copyright (C) 2023 Jeremie Salvi.
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||
A copy of the license is included in the section entitled "GNU
|
||||
Free Documentation License".
|
||||
|
||||
Premiers tests et emulation QEMU
|
||||
================================
|
||||
|
||||
Espressif fournit une version de QEMU modifiée pour tester du code. Cela peut avoir
|
||||
plusieurs avantages :
|
||||
|
||||
* Commencer à coder sans attendre la livraison de notre ESP.
|
||||
* Tester du code plus rapidement sans avoir à flasher notre ESP.
|
||||
* Coder toute la partie web en local sur notre machine.
|
||||
* Attacher un debugeur, utiliser des berakpoints, et débuger plus facilement.
|
||||
|
||||
Pour cela, nous reprendront le `code de la partie précédente </esp32/vscodium_integration.html#configuration-de-l-extension-c-c>`_.
|
||||
|
||||
Première Execution
|
||||
------------------
|
||||
|
||||
On reprend le projet vu dans l'intégration vscodium. Pout lancer QEMU il nous faut construire la mémonire
|
||||
flash pour la donner à QEMU qu'elle puisse démarrer dessus.
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python /opt/esp/esp-idf/components/esptool_py/esptool/esptool.py \
|
||||
--chip esp32 merge_bin --output flash_img.bin \
|
||||
--fill-flash-size 4MB --flash_mode dio --flash_size keep --flash_freq 40m \
|
||||
0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/new_project.bin
|
||||
|
||||
Voilà, il ne nous reste plus qu'à lancer QEMU à partir de cette image !
|
||||
|
||||
Emulation du système dans une machine QEMU
|
||||
------------------------------------------
|
||||
|
||||
Il suffit maintenant de lancer notre machine QEMU à partir de la flash que l'on vient de créer :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
/opt/qemu/build/qemu-system-xtensa -nographic -machine esp32 \
|
||||
-drive file=flash_img.bin,if=mtd,format=raw
|
||||
|
||||
Et voilà, on emule la machine et on à la sortie série qui s'affiche. Pour quitter QEMU, on utilise
|
||||
``Ctrl-A x``
|
||||
|
||||
Flasher ou monitorer en emulant un port série
|
||||
---------------------------------------------
|
||||
|
||||
Cela ne va pas beaucoup nous servir, puisqu'il suffit de relancer qemu avec notre nouvelle
|
||||
image du flash, mais il faut savoir que c'est possible en emulant les GPIO de notre ESP.
|
||||
|
||||
on à deux configurations :
|
||||
|
||||
* ``-global driver=esp32.gpio,property=strap_mode,value=0x12`` pour monitorer la sortie.
|
||||
* ``-global driver=esp32.gpio,property=strap_mode,value=0xf`` pour flasher la carte.
|
||||
|
||||
Attention, le port série ne peut pas recevoir deux connexions en même temps, il faut donc
|
||||
couper le moniteur pour flasher et vice versa.
|
||||
|
||||
Monitorer QEMU
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
On commence par lancer QEMU avec le port série en mode monitor.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
/opt/qemu/build/qemu-system-xtensa -nographic -machine esp32 \
|
||||
-drive file=flash_img.bin,if=mtd,format=raw \
|
||||
-global driver=esp32.gpio,property=strap_mode,value=0x12 \
|
||||
-serial tcp::5555,server,nowait
|
||||
|
||||
On lance ensuite le moniteur
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
idf.py --port socket://localhost:5555 monitor
|
||||
|
||||
Flasher QEMU
|
||||
~~~~~~~~~~~~
|
||||
|
||||
On commence par lancer QEMU avec le port série en mode flash.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
/opt/qemu/build/qemu-system-xtensa -nographic -machine esp32 \
|
||||
-drive file=flash_img.bin,if=mtd,format=raw \
|
||||
-global driver=esp32.gpio,property=strap_mode,value=0xf \
|
||||
-serial tcp::5555,server,nowait \
|
||||
|
||||
On flash ensuite notre machine
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
idf.py --port socket://localhost:5555 flash
|
||||
|
||||
Debuger grâce de QEMU avec gdb
|
||||
------------------------------
|
||||
|
||||
Pour attacher GDB, on lance la commande avec ``-s -S`` qui va nous ouvrir un socket tcp sur le port 1234.
|
||||
pour changer de port, on utilise : ``-gdb tcp::1235``
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
/opt/qemu/build/qemu-system-xtensa -nographic -machine esp32 \
|
||||
-drive file=flash_img.bin,if=mtd,format=raw \
|
||||
-gdb tcp:localhost:1235 -S
|
||||
|
||||
On peut ensuite lancer le debugeur fourni avec l'API, mettre des breakpoints, etc.
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
|
||||
/root/.espressif/tools/xtensa-esp-elf-gdb/12.1_20221002/xtensa-esp-elf-gdb/bin/xtensa-esp32-elf-gdb build/main.elf
|
||||
|
||||
(gdb) b app_main
|
||||
# >> Breakpoint 1 at 0x400d5170: file /root/test/sample_project/main/main.c, line 7.
|
||||
(gdb) target remote :1235
|
||||
# >> Remote debugging using :1235
|
||||
# >> 0x40000400 in ?? ()
|
||||
(gdb) c
|
||||
# Continuing.
|
||||
# Thread 1 hit Breakpoint 1, app_main () at /root/test/sample_project/main/main.c:7
|
||||
(gdb) n
|
||||
# >> 8 int count = 0;
|
||||
(gdb) n
|
||||
# >> 11 ESP_LOGI("FIRST TRY", "Test loop pass n° %d", count++);
|
||||
(gdb) b
|
||||
# >> Breakpoint 2 at 0x400d5175: file /root/test/sample_project/main/main.c, line 11.
|
||||
(gdb) c
|
||||
# >> Continuing.
|
||||
# >> Thread 1 hit Breakpoint 2, app_main () at /root/test/sample_project/main/main.c:11
|
||||
# >> 11 ESP_LOGI("FIRST TRY", "Test loop pass n° %d", count++);
|
||||
(gdb) c
|
||||
# >> Continuing.
|
||||
# >> Thread 1 hit Breakpoint 2, app_main () at /root/test/sample_project/main/main.c:11
|
||||
# >> 11 ESP_LOGI("FIRST TRY", "Test loop pass n° %d", count++);
|
||||
(gdb) print count
|
||||
# >> $1 = 2
|
||||
(gdb) set count=100
|
||||
(gdb) c
|
||||
# >> Continuing.
|
||||
# >> Thread 1 hit Breakpoint 2, app_main () at /root/test/sample_project/main/main.c:11
|
||||
# >> 11 ESP_LOGI("FIRST TRY", "Test loop pass n° %d", count++);
|
||||
(gdb) quit
|
||||
|
||||
.. image:: ../images/esp32/QEMU_gdb_example.png
|
||||
:width: 400
|
||||
|
||||
Voilà. On va voir plus tard comment configurer cela dans vscodium
|
||||
|
||||
|
||||
Intégration dans vscodium
|
||||
-------------------------
|
||||
|
||||
On arrive ici à ce qui pour moi donne une vraie utilité à configurer un environnement QEMU.
|
||||
On va pouvoir à partir de vscode gérer nos breakpoint, naviguer dans le code, avoir sous
|
||||
les yeux les variables et les call stack, etc.
|
||||
|
||||
Création des tâches
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Je ne réexplique pas tout, on à déjà vu le fonctionnement dans la
|
||||
`partie précédente </esp32/vscodium_integration.html#configurer-des-taches>`_
|
||||
On rajoute quelques fonctions suivantes à notre script et on ajoute les tâches pour
|
||||
lancer le débug et l'execution avec QEMU.
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
|
||||
function kill_qemu {
|
||||
for i in $(ps -elf | grep 'qemu-system-xtensa' | awk '{print $4}')
|
||||
do
|
||||
echo "found process $i"
|
||||
kill $i
|
||||
done
|
||||
}
|
||||
|
||||
function qemu_execute {
|
||||
kill_qemu
|
||||
python /opt/esp/esp-idf/components/esptool_py/esptool/esptool.py \
|
||||
--chip esp32 merge_bin --output flash_img.bin \
|
||||
--fill-flash-size 4MB --flash_mode dio --flash_size keep --flash_freq 40m \
|
||||
0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin \
|
||||
0x10000 build/new_project.bin
|
||||
/opt/qemu/build/qemu-system-xtensa -nographic -machine esp32 \
|
||||
-drive file=flash_img.bin,if=mtd,format=raw
|
||||
exit 0
|
||||
}
|
||||
|
||||
function qemu_debug {
|
||||
kill_qemu
|
||||
python /opt/esp/esp-idf/components/esptool_py/esptool/esptool.py \
|
||||
--chip esp32 merge_bin --output flash_img.bin \
|
||||
--fill-flash-size 4MB --flash_mode dio --flash_size keep --flash_freq 40m \
|
||||
0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin \
|
||||
0x10000 build/new_project.bin
|
||||
/opt/qemu/build/qemu-system-xtensa -nographic -machine esp32 \
|
||||
-drive file=flash_img.bin,if=mtd,format=raw -s -S
|
||||
exit 0
|
||||
}
|
||||
|
||||
#######################
|
||||
# Main
|
||||
#######################
|
||||
|
||||
case $1 in
|
||||
kill_idf)
|
||||
echo "Killing all idf process"
|
||||
kill_idf
|
||||
;;
|
||||
kill_qemu)
|
||||
echo "Killing all idf process"
|
||||
kill_qemu
|
||||
;;
|
||||
qemu_execute)
|
||||
echo "Execute with QEMU"
|
||||
qemu_execute
|
||||
;;
|
||||
qemu_debug)
|
||||
echo "Debug with QEMU"
|
||||
qemu_debug
|
||||
;;
|
||||
*)
|
||||
echo "Missing args"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
.. code-block:: json
|
||||
:linenos:
|
||||
|
||||
{
|
||||
"label": "Execute with QEMU",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/.scripts/tasks.sh",
|
||||
"args": [
|
||||
"qemu_execute"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new",
|
||||
"close": true
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Debug with QEMU",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/.scripts/tasks.sh",
|
||||
"args": [
|
||||
"qemu_debug"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new",
|
||||
"close": true
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
|
||||
Intégration de gdb dans vscodium
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Pour cela, il faut éditer le fichier ``.vscode/launch.json``
|
||||
|
||||
.. code-block:: json
|
||||
:linenos:
|
||||
|
||||
{
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"type": "gdb",
|
||||
"gdbpath": "/root/.espressif/tools/xtensa-esp-elf-gdb/12.1_20221002/xtensa-esp-elf-gdb/bin/xtensa-esp32-elf-gdb",
|
||||
"postDebugTask": "kill QEMU process",
|
||||
"request": "attach",
|
||||
"name": "Attach to QEMU",
|
||||
"executable": "${workspaceFolder}/build/main.elf",
|
||||
"target": ":1234",
|
||||
"remote": true,
|
||||
"cwd": "${workspaceRoot}",
|
||||
"valuesFormatting": "parseText"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Voilà, on n'a plus qu'à lancer la tâche ``Debug with QEMU`` et de lancer le débugeur.
|
||||
|
||||
.. image:: ../images/esp32/debug.png
|
||||
:width: 600
|
||||
|
||||
On se retrouve avec les breakpoints, les call stacks, les variables et la sortie tous affichés sans notre
|
||||
environnement de travail 💪.
|
||||
|
||||
Prochaine étape
|
||||
---------------
|
||||
|
||||
Les interactions avec l'électronique devant être faites avec un vrai ESP, le développement
|
||||
sous QEMU ne nous sert à rien si on ne peut pas coder la partie réseau et communications depuis le PC.
|
||||
Prochaine étape donc, faire tourner l'emulation avec une interface réseau pour coder notre serveur web
|
||||
embarqué.
|
||||
203
esp32/qemu_ethernet.rst
Normal file
203
esp32/qemu_ethernet.rst
Normal file
@@ -0,0 +1,203 @@
|
||||
..
|
||||
Copyright (C) 2023 Jeremie Salvi.
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||
A copy of the license is included in the section entitled "GNU
|
||||
Free Documentation License".
|
||||
|
||||
Interface ethernet avec QEMU
|
||||
============================
|
||||
|
||||
Y a pas à dire, j'ai bien galéré pour cette partie 😅. Bon, faut dire q'il y avait des choses qui
|
||||
ne fonctionnent pas. J'y refient à la fin de cette section.
|
||||
|
||||
Après pas mal de tatonnements entre la doc et les exemples fournis par espressif, j'ai finalement
|
||||
réussi à interagir entre firefox et la machine QEMU. Il y a plusieures choses à bien comprendre
|
||||
dans la façon dont fonctionne l'IDF, notament comment sont gérées les
|
||||
`différentes couches du modèle OSI <https://fr.wikipedia.org/wiki/Mod%C3%A8le_OSI>`_.
|
||||
|
||||
Principes
|
||||
---------
|
||||
|
||||
dans l'ESP, il y à trois étapes principales pour le réseau :
|
||||
|
||||
#. La partie physique (couches physique + mac),
|
||||
#. La partie network (couches network + transport),
|
||||
#. La partie link entre physique et network.
|
||||
|
||||
La partie physique
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
C'est la couche physique plus la couche mac. On la configure, on la démare, et ensuite on
|
||||
la link à la partie network. Côté wifi, comme le driver de la carte est toujours le même,
|
||||
on à la fonction ``esp_netif_create_default_wifi_sta()`` qui gère tout, elle créer tout.
|
||||
Elle gère la partie physique, la link à la partie network, et nous retourne un handle de la
|
||||
partie network.
|
||||
|
||||
Pour l'ethernet, c'est différent, comme plusieurs extensions ethernet sont disponible pour
|
||||
l'esp32, il y a plusieurs drivers disponibles, il nous faut donc choisir le bon, et configurer ça à la main.
|
||||
|
||||
Dans notre cas, pour QEMU, le driver à utiliser est le driver openeth. (`cf wiki qemu <https://github.com/espressif/qemu/wiki>`_)
|
||||
La configuration de la partie physique se fait à l'aide des fonctions suivantes :
|
||||
|
||||
.. code-block:: cpp
|
||||
:linenos:
|
||||
|
||||
// Pysical layer configuration
|
||||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||
phy_config.phy_addr = -1;
|
||||
phy_config.reset_gpio_num = -1;
|
||||
phy_config.autonego_timeout_ms = 4000;
|
||||
s_phy = esp_eth_phy_new_dp83848(&phy_config);
|
||||
// Data link layer configuration
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
mac_config.rx_task_stack_size = 4096;
|
||||
mac_config.rx_task_prio = 20;
|
||||
s_mac = esp_eth_mac_new_openeth(&mac_config);
|
||||
// Install Ethernet driver
|
||||
esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy);
|
||||
esp_eth_driver_install(&config, &esp_eth_handle);
|
||||
|
||||
La partie network
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Can cette partie, on gère la couche ip et la couche transport (TCP/UDP/ICMP). Espressif nous donne pour ça tout un tas
|
||||
de fonctions, qui sont un wrapper des librairies opensource `lwIP <https://fr.wikipedia.org/wiki/LwIP>`_ (lightweight IP).
|
||||
|
||||
Elle nous permet d'initialiser ces couches, de gérer l'attribution de l'adressage ip (statique ou DHCP.)
|
||||
|
||||
Cette couche s'implémente avec les fonctions suivantes :
|
||||
|
||||
.. code-block:: cpp
|
||||
:linenos:
|
||||
|
||||
// Initialize TCP/IP layers
|
||||
esp_netif_init();
|
||||
esp_event_loop_create_default();
|
||||
esp_netif_config_t esp_netif_config = ESP_NETIF_DEFAULT_ETH();
|
||||
esp_netif_t *eth_netif = esp_netif_new(&esp_netif_config);
|
||||
|
||||
// Set static ip
|
||||
// esp_netif_dhcpc_stop(eth_netif);
|
||||
// esp_netif_ip_info_t esp_netif_ip_info;
|
||||
// esp_netif_ip_info.ip.addr = ESP_IP4TOUINT32(11,2,168,192);
|
||||
// esp_netif_ip_info.netmask.addr = ESP_IP4TOUINT32(0,255,255,255);
|
||||
// esp_netif_ip_info.gw.addr = ESP_IP4TOUINT32(1,2,168,192);
|
||||
// esp_err_t ret2 = esp_netif_set_ip_info(eth_netif, &esp_netif_ip_info);
|
||||
// ESP_ERROR_CHECK(ret2);
|
||||
|
||||
Lien entre les deux premières couches
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
On peut configurer les deux parties précédentes dans l'ordre de notre choix, mais c'est seulement une fois que
|
||||
ces deux étapes sont réalisées qu'on peut attacher la couche TCP/IP à la couche physique/MAC.
|
||||
|
||||
Assez simple à faire, il suffit d'utiliser le code suivant :
|
||||
|
||||
.. code-block:: cpp
|
||||
:linenos:
|
||||
|
||||
// Bind TCP/IP to Phy+MAC
|
||||
esp_eth_netif_glue_handle = esp_eth_new_netif_glue(esp_eth_handle);
|
||||
esp_netif_attach(eth_netif, esp_eth_netif_glue_handle);
|
||||
|
||||
On peut ensuite démarrer ethernet avec la fonction suivante :
|
||||
|
||||
.. code-block:: cpp
|
||||
:linenos:
|
||||
|
||||
// Start ethernet
|
||||
esp_eth_start(esp_eth_handle);
|
||||
|
||||
La partie applicative
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Ici, on aura l'implémentation de notre serveur web. Ça se fait à partir de la librairie ``esp_http_server``
|
||||
Rien de bien complexe ici quand on connait bien le fonctionnement de ce protocole.
|
||||
|
||||
Le code est le suivant :
|
||||
|
||||
.. code-block:: cpp
|
||||
:linenos:
|
||||
|
||||
esp_err_t get_handler(httpd_req_t *req)
|
||||
{
|
||||
/* Send a simple response */
|
||||
const char resp[] = "URI GET Response\n";
|
||||
httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);
|
||||
ESP_LOGI(TAG, "good");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
httpd_uri_t uri_get = {
|
||||
.uri = "/uri",
|
||||
.method = HTTP_GET,
|
||||
.handler = get_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
Une fois ces trois étapes franchies, on peut démarrer QEMU avec la commande suivante :
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
|
||||
/opt/qemu/build/qemu-system-xtensa -m 128 -nographic -machine esp32 \
|
||||
-drive file=flash_img.bin,if=mtd,format=raw \
|
||||
-net nic,model=open_eth \
|
||||
-net user,host=192.168.2.1,net=192.168.2.0/24,hostfwd=tcp::80-:80 \
|
||||
-net tap,script=no,downscript=no
|
||||
|
||||
On teste avec un simple ``curl http://127.0.0.1/uri`` et on voit que ça marche.
|
||||
|
||||
.. image:: ../images/esp32/QEMU_http_response.png
|
||||
:width: 600
|
||||
|
||||
Ce qui ne marche pas
|
||||
--------------------
|
||||
|
||||
L'IPv6
|
||||
~~~~~~
|
||||
|
||||
Ne marche pas ni en wifi avec SLAAC, en wifi avec une ip statique,
|
||||
ni avec QEMU. C'est ce qui m'a le plus fait perdre de temps.
|
||||
En m'acharnant, je suis tombé sur ça 😤 🤬:
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
|
||||
esp_err_t esp_netif_add_ip6_address(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr, uint8_t preference)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Il manque clairement du travail du côté d'espressif pour implémenter la couche IPv6. C'est embêtant, ça nous
|
||||
oblige à faire pleins de routes dans notre box, alors qu'on pourrait tout simplement pinger notre domotique de
|
||||
l'extérieur avec du v6
|
||||
|
||||
.. note:: Il n'y a que la librairie loobback.c qui manque d'implémentation, dans
|
||||
components/lwip/lwip/src/core/netif.c on trouve
|
||||
``netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chosen_idx)``
|
||||
qui est bel et bien implémentée. J'ai perdu du temps dessus, ça sera un NAT v4 pour l'instant.
|
||||
Si à l'avenir j'arrive à faire marcher l'IPv6, je le documenterai.
|
||||
|
||||
L'interface tun0 et le bridging
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
auand je lance ma commande QEMU, j'ai deux moyens d'accéder à mon ``http_server`` :
|
||||
Soit via le ``hostfwd=tcp::80-:80``, soit via ``-net tap,script=no,downscript=no``.
|
||||
|
||||
Je fait un ``ip link set tap0 up et brctl addif br0 tap0`` pour que mon tap0 soit
|
||||
pingable depuis le bridge en 192.168.2.15.
|
||||
|
||||
Et bien, un ``curl http://127.0.0.1/uri`` marche très bien, un ``curl http://192.168.2.15/uri``
|
||||
ne parche pas, on à une erreur ``W (476492) opencores.emac: emac_opencores_isr_handler: RX frame dropped (0x14)``
|
||||
J'ai essayé de mettre au max les buffers RX, testé toutes les configurations possible et imaginables,
|
||||
impossible de passer par le bridge sans avoir un frame_drop. Grace au mod débug, j'arrive ici :
|
||||
|
||||
``components/esp_eth_src/esp_eth_mac_openeth.c``
|
||||
|
||||
.. image:: ../images/esp32/openeth_busy.png
|
||||
:width: 600
|
||||
|
||||
313
esp32/vscodium_integration.rst
Normal file
313
esp32/vscodium_integration.rst
Normal file
@@ -0,0 +1,313 @@
|
||||
..
|
||||
Copyright (C) 2023 Jeremie Salvi.
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||
A copy of the license is included in the section entitled "GNU
|
||||
Free Documentation License".
|
||||
|
||||
Création d'un environnement vscodium
|
||||
====================================
|
||||
|
||||
Le but ici va être d'automatiser un maximum d'actions, comme par exemple la compilation, le débug,
|
||||
tout en les intégrant dans vscodium. Nous allons aussi configurer l'autocomplétion, la navigation
|
||||
dans les fonctions et les librairies, et tout ce qui peut rendre notre IDE plus ergonomique.
|
||||
|
||||
Je vais essayer de maintenir cette section à jour au fur et à mesure que j'améliore
|
||||
le projet.
|
||||
|
||||
.. warning:: pensez à importer les variables d'environnement avec esp_idf_import
|
||||
comme vu dans l'installation si nécessaire.
|
||||
|
||||
Créer un nouveau project
|
||||
------------------------
|
||||
|
||||
On commence par créer un projet a l'aide le l'utilitaire IDF :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
idf.py create-project new_project
|
||||
|
||||
On peut ensuite ouvrir vscodium et ouvrir ce dossier ``Ctrl+O`` ou ``File -> Open Folder``.
|
||||
|
||||
|
||||
Déclarer le $PATH et les variables d'environnement pour le terminal
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Si on lance notre terminal vscode et qu'on fait un ``export``, on ne retrouve pas les variables
|
||||
d'environnement nécessaires à bon fonctionnement des outils IDF. Pour cela, il faut les renseigner
|
||||
dans notre espace de travail.
|
||||
|
||||
Pour cela, on fait ``F1`` et on cherche settings.json.
|
||||
|
||||
.. image:: ../images/esp32/settings.json.png
|
||||
:width: 600
|
||||
|
||||
Il va nous ouvrir le fichier ``.vscode/settings.json`` qui va contenir nos paramètres
|
||||
propre au projet. Dans celui ci, on va renseigner toutes les variables d'environnement
|
||||
qu'on à après un ``esp_idf_import`` comme vu dans la partie
|
||||
`installation </esp32/installation.html#installation-et-configuration-de-l-esp-idf>`_.
|
||||
|
||||
On ajoute les lignes suivantes :
|
||||
|
||||
.. code-block:: json
|
||||
:linenos:
|
||||
|
||||
{
|
||||
"C_Cpp.intelliSenseEngine": "Tag Parser",
|
||||
"terminal.integrated.cwd": "/root/test/new_project",
|
||||
// environnement variables needeed by ESP_IDF
|
||||
"terminal.integrated.env.linux": {
|
||||
"ESP_IDF_VERSION": "5.1",
|
||||
"ESP_ROM_ELF_DIR": "/opt/esp/esp-idf/.espressif/tools/esp-rom-elfs/20220823/",
|
||||
"IDF_DEACTIVATE_FILE_PATH": "/tmp/tmpxj9n6nndidf_46523",
|
||||
"IDF_PATH": "/opt/esp/esp-idf",
|
||||
"IDF_PYTHON_ENV_PATH": "/opt/esp/esp-idf/.espressif/python_env/idf5.1_py3.10_env",
|
||||
"IDF_TOOLS_EXPORT_CMD": "/opt/esp/esp-idf/export.sh",
|
||||
"IDF_TOOLS_INSTALL_CMD": "/opt/esp/esp-idf/install.sh",
|
||||
"IDF_TOOLS_PATH": "/opt/esp/esp-idf/.espressif",
|
||||
"OPENOCD_SCRIPTS": "/opt/esp/esp-idf/.espressif/tools/openocd-esp32/v0.11.0-esp32-20221026/openocd-esp32/share/openocd/scripts",
|
||||
"PATH": "/opt/esp/esp-idf/components/esptool_py/esptool:/opt/esp/esp-idf/components/espcoredump:/opt/esp/esp-idf/components/partition_table:/opt/esp/esp-idf/components/app_update:/opt/esp/esp-idf/.espressif/tools/xtensa-esp-elf-gdb/12.1_20221002/xtensa-esp-elf-gdb/bin:/opt/esp/esp-idf/.espressif/tools/xtensa-esp32-elf/esp-2022r1-11.2.0/xtensa-esp32-elf/bin:/opt/esp/esp-idf/.espressif/tools/esp32ulp-elf/2.35_20220830/esp32ulp-elf/bin:/opt/esp/esp-idf/.espressif/tools/openocd-esp32/v0.11.0-esp32-20221026/openocd-esp32/bin:/opt/esp/esp-idf/.espressif/python_env/idf5.1_py3.10_env/bin:/opt/esp/esp-idf/tools:/usr/local/sbin:/usr/local/bin:/usr/bin"
|
||||
},
|
||||
}
|
||||
|
||||
Configuration de l'extension C/C++
|
||||
----------------------------------
|
||||
|
||||
Commencons par un simple code pour vérifier que tout fonctionne :
|
||||
|
||||
.. code-block:: c
|
||||
:linenos:
|
||||
|
||||
#include <stdio.h>
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
int count=0;
|
||||
while (true)
|
||||
{
|
||||
ESP_LOGI("NEW PROJECT", "my first test %d", count++);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
}
|
||||
|
||||
On teste notre code avec les commandes suivantes :
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
|
||||
idf.py set-target esp32
|
||||
idf.py build
|
||||
idf.py flash
|
||||
idf.py monitor
|
||||
|
||||
Pour quitter le moniteur, il suffit d'un ``Ctrl+]``.
|
||||
|
||||
Ok, une fois que tout marche, on peut quitter le terminal.
|
||||
On voit que vscodium affiche des erreurs au niveau des include qu'il ne trouve pas. Réglons tout ça.
|
||||
|
||||
En appuyant sur ``F1`` et en cherchant ``C/C++ configuration`` on trouve soit l'interface utilisateur soit un
|
||||
racourci pour créer et renseigner le fichier ``.vscode/c_cpp_properties.json``.
|
||||
Pour que la recherche intelligente foncrionne, il faut configurer l'extension pour qu'elle
|
||||
sache ou chercher les librairies fournies par l'IDE. Des templates sont déjà disponibles
|
||||
sur `le github d'espressif <https://github.com/espressif/vscode-esp-idf-extension/tree/master/templates/.vscode>`_.
|
||||
|
||||
|
||||
.. image:: ../images/esp32/c_cpp_config.png
|
||||
:width: 600
|
||||
|
||||
En m'inspirant de ce qu'esperssif fournit pour son extension vscodium,
|
||||
voilà à quoi je suis arrivé pour que tout fonctionne bien :
|
||||
|
||||
.. code-block:: json
|
||||
:linenos:
|
||||
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ESP_IDF",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"/opt/esp/esp-idf/components/**",
|
||||
"/opt/esp/esp-idf/.espressif/tools/**"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"${workspaceFolder}/**",
|
||||
"/opt/esp/esp-idf/components/**",
|
||||
"/opt/esp/esp-idf/.espressif/tools/**"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
|
||||
Voilà, ça c'est pour que vscode et intellisense trouvent toutes les ressources dont ils ont besoin.
|
||||
|
||||
|
||||
Configurer des tâches
|
||||
---------------------
|
||||
|
||||
C'est ici, à mon sens, la fonctionnalié qui donne le plus de confort et qui va nous faire gagner un temps précieux.
|
||||
Dans vscodium, on peut définir des tâches personnalisées pour le projet en cours, et les executer rapidement avec un raccourci clavier.
|
||||
|
||||
Dans un premier temps, je créer un script dans ``.scripts/tasks.sh`` que je rend executable, et dans lequel je vais coder toutes les actions
|
||||
que je vais répéter souvent, comme builder le code, flasher l'esp, lancer QEMU pour débuger, etc. Voici le code, il est améliorable bien
|
||||
entendu et on va y rajouter toutes les fonctionnalités que nous voudrons par la suite.
|
||||
|
||||
.. code-block:: bash
|
||||
:linenos:
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
function kill_idf {
|
||||
for i in $(ps -elf | grep 'idf.py' | awk '{print $4}')
|
||||
do
|
||||
echo "found process $i"
|
||||
kill $i
|
||||
done
|
||||
}
|
||||
|
||||
function build {
|
||||
$IDF_PATH/tools/idf.py build
|
||||
}
|
||||
|
||||
function flash {
|
||||
kill_idf
|
||||
|
||||
$IDF_PATH/tools/idf.py flash
|
||||
exit 0
|
||||
}
|
||||
|
||||
function monitor {
|
||||
kill_idf
|
||||
$IDF_PATH/tools/idf.py monitor
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
#######################
|
||||
# Main
|
||||
#######################
|
||||
|
||||
case $1 in
|
||||
kill_idf)
|
||||
echo "Killing all idf process"
|
||||
kill_idf
|
||||
;;
|
||||
build)
|
||||
echo "Building project"
|
||||
build
|
||||
;;
|
||||
flash)
|
||||
echo "Flashing memory"
|
||||
flash
|
||||
;;
|
||||
monitor)
|
||||
echo "Monitoring output"
|
||||
monitor
|
||||
;;
|
||||
*)
|
||||
echo "Missing args"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
Voilà, une fois ce script fait, on va y assigner des tâches vscodium. Je vais aller vite sur les explications, je
|
||||
vous met un lien vers `la documentation officielle du fichier tasks.json <https://code.visualstudio.com/docs/editor/tasks>`_.
|
||||
|
||||
On créer un fichier ``.vscode/tasks.json`` et on y renseigne le code suivant :
|
||||
|
||||
.. code-block:: json
|
||||
:linenos:
|
||||
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "kill idf.py process",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/.scripts/tasks.sh",
|
||||
"args": [
|
||||
"kill_idf"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"close": false
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "build project",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/.scripts/tasks.sh",
|
||||
"args": [
|
||||
"build"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"close": false
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "flash esp memory",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/.scripts/tasks.sh",
|
||||
"args": [
|
||||
"flash"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"close": false
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "monitor",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/.scripts/tasks.sh",
|
||||
"args": [
|
||||
"monitor"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new",
|
||||
"close": true
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "build, flash, and monitor",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": ["build project", "flash esp memory", "monitor"],
|
||||
"problemMatcher": []
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
Et voilà, comme vous pouvez le voir, vous avez toutes les tâches de compilation, execution, et debug imtégrées à
|
||||
vscodium. on peut y accéder en tappant ``F1 -> run task``, et pour gagner du temps, on peut même définir un raccourci clavier
|
||||
Pour gagner encore une étape.
|
||||
|
||||
.. image:: ../images/esp32/run_tasks.png
|
||||
:width: 600
|
||||
|
||||
Et voilà, un appui sur ``Ctrl+F1`` me donne accès à autant de tâches que je voidrai en créer.
|
||||
|
||||
.. image:: ../images/esp32/tasks.png
|
||||
:width: 600
|
||||
Reference in New Issue
Block a user