Blogons mon bon

En réfléchissant comment relancer un peu ce projet de boite à histoires je suis parvenu à la conclusion qu’il fallait le documenter sur une base régulière. Pour ce faire je vais donc m’astreindre à écrire des billets de blog sur une base la plus régulière possible (mais je ne me mouille pas sur la fréquence encore ;).

Quoi de mieux pour maîtriser un sujet que d’en parler !

Alors, qu’est-ce que le raconteur ?

la boite à histoire terminée en photo
Première version du raconteur version «palette» car fabriqué à partir de bois de palette.

C’est une boite à histoire «faites maison» qui utilise deux modules électroniques autonome :

Le lecteur MP3

Le lecteur MP3

Le module MP3 est conçu pour fonctionner de manière parfaitement autonome si on lui branche un haut parleur, une alimentation et des boutons poussoir. Les musiques à jouer se trouvent elles sur la carte microSD. Pour le raconteur nous utiliserons le port série RX/TX pour le piloter au moyen de la Longan Nano.

La carte de pilotage Longan Nano

Schema de branchement de la carte Longan Nano à base de GD32VF103

La carte Longan Nano est programmable par le ports usb-c, elle est elle aussi munie d’un lecteur de microSD. Cette microSD va nous servir à stocker la description des histoires que le lecteur de MP3 va jouer. Comme le kit proposé sur Aliexpress est munie d’un écran amoled nous nous en servirons pour afficher des images représentant l’histoire à jouer et/ou une étape du menu.

Les deux cartes microSD

Je n’ai pas encore donné les détail de la programmation de la longan nano qui pilote l’ensemble mais un problème saute déjà au yeux : Il y a besoin de deux cartes microSD.

  • Une carte contenant les histoires (ensemble de fichier MP3 lisibles par le DFPLayer mini)
  • Une carte contenant la description des histoires, les images des menus ainsi que l’arborescence de navigation

Le programme est quand à lui téléchargé dans la mémoire flash du microcontrôleur.

Ça fait beaucoup de données à compiler et formater pour chaque histoire. D’où l’idée de créer un logiciel pour télécharger, cuisiner et servir les histoires pour le raconteur.

Ce logiciel est en cours de développement également et se nomme Recette d’Histoire. Il est codé en Rust (parce que j’ai envie d’apprendre le Rust) et se base sur des «recettes» présentées sous forme de fichier Yaml.

Mais sa description fera l’objet d’un futur billet de blog.

Maturité de Rust

L’utilitaire permettant de générer les histoires à télécharger dans le Raconteur se nomme RecetteDHistoire. Il est développé en Rust. C’est une bonne excuse pour apprendre ce nouveau langage.

Un des reproche actuel fait à ce langage de programmation système est qu’il n’est pas encore très mature, ses interfaces ne sont pas encore très stable.

Pour le moment, le développement de RecetteDHistoire n’est pas ralenti par des problèmes de stabilitées de ses interfaces. L’apprentissage du langage lui même est déjà suffisamment chronophage 😉

On peut noter cependant que les versions des bibliothèques utilisées (on parle de crate dans le langage du crabe) sont majoritairement en dessous de leurs version 1.0.0 lorsque l’on compile le logiciel:

$ cargo build
   Compiling autocfg v1.1.0
   Compiling libc v0.2.132
   Compiling cfg-if v1.0.0
   Compiling memchr v2.5.0
   Compiling proc-macro2 v1.0.43
   Compiling unicode-ident v1.0.3
   Compiling quote v1.0.21
   Compiling syn v1.0.99
   Compiling cc v1.0.73
   Compiling once_cell v1.13.1
   Compiling log v0.4.17
   Compiling bitflags v1.3.2
   Compiling futures-core v0.3.23
   Compiling adler v1.0.2
   Compiling futures-sink v0.3.23
   Compiling pin-project-lite v0.2.9
   Compiling scopeguard v1.1.0
   Compiling unicode-width v0.1.9
   Compiling crossbeam-utils v0.8.11
   Compiling pkg-config v0.3.25
   Compiling gimli v0.26.2
   Compiling futures-task v0.3.23
   Compiling bytes v1.2.1
   Compiling glob v0.3.0
   Compiling cfg-if v0.1.10
   Compiling version_check v0.1.5
   Compiling futures-channel v0.3.23
   Compiling futures-util v0.3.23
   Compiling regex-syntax v0.6.27
   Compiling proc-macro2 v0.4.30
   Compiling lazy_static v1.4.0
   Compiling rustc-demangle v0.1.21
   Compiling hashbrown v0.12.3
   Compiling fnv v1.0.7
   Compiling quick-error v1.2.3
   Compiling futures-io v0.3.23
   Compiling termcolor v1.1.3
   Compiling pin-utils v0.1.0
   Compiling rayon-core v1.9.3
   Compiling byteorder v1.4.3
   Compiling itoa v1.0.3
   Compiling unicode-xid v0.1.0
   Compiling foreign-types-shared v0.1.1
   Compiling openssl v0.10.41
   Compiling ansi_term v0.12.1
   Compiling crc32fast v1.3.2
   Compiling strsim v0.8.0
   Compiling serde_derive v1.0.144
   Compiling bindgen v0.49.4
   Compiling vec_map v0.8.2
   Compiling matches v0.1.9
   Compiling peeking_take_while v0.1.2
   Compiling shlex v0.1.1
   Compiling tinyvec_macros v0.1.0
   Compiling serde v1.0.144
   Compiling either v1.8.0
   Compiling native-tls v0.2.10
   Compiling smallvec v1.9.0
   Compiling httparse v1.7.1
   Compiling io-lifetimes v0.7.3
   Compiling rustix v0.35.9
   Compiling percent-encoding v2.1.0
   Compiling openssl-probe v0.1.5
   Compiling try-lock v0.2.3
   Compiling encoding_rs v0.8.31
   Compiling tower-service v0.3.2
   Compiling httpdate v1.0.2
   Compiling linux-raw-sys v0.0.46
   Compiling weezl v0.1.7
   Compiling ryu v1.0.11
   Compiling unicode-bidi v0.3.8
   Compiling adler32 v1.2.0
   Compiling os_str_bytes v6.3.0
   Compiling endian-type v0.1.2
   Compiling linked-hash-map v0.5.6
   Compiling bit_field v0.10.1
   Compiling lebe v0.5.2
   Compiling color_quant v1.1.0
   Compiling half v1.8.2
   Compiling unicode-segmentation v1.9.0
   Compiling ipnet v2.5.0
   Compiling utf8parse v0.2.0
   Compiling textwrap v0.15.0
   Compiling scoped_threadpool v0.1.9
   Compiling bytemuck v1.12.1
   Compiling strsim v0.10.0
   Compiling number_prefix v0.3.0
   Compiling bytes v0.5.6
   Compiling pin-project-lite v0.1.12
   Compiling mime v0.3.16
   Compiling base64 v0.13.0
   Compiling main_error v0.1.2
   Compiling tracing-core v0.1.29
   Compiling miniz_oxide v0.5.3
   Compiling slab v0.4.7
   Compiling memoffset v0.6.5
   Compiling crossbeam-epoch v0.9.10
   Compiling indexmap v1.9.1
   Compiling tokio v1.20.1
   Compiling lock_api v0.4.7
   Compiling num-traits v0.2.15
   Compiling rayon v1.5.3
   Compiling num-integer v0.1.45
   Compiling num-rational v0.4.1
   Compiling textwrap v0.11.0
   Compiling getopts v0.2.21
   Compiling nom v4.2.3
   Compiling clang-sys v0.28.1
   Compiling humantime v1.3.0
   Compiling openssl-sys v0.9.75
   Compiling backtrace v0.3.66
   Compiling libloading v0.5.2
   Compiling fxhash v0.2.1
   Compiling http v0.2.8
   Compiling foreign-types v0.3.2
   Compiling tinyvec v1.6.0
   Compiling itertools v0.10.3
   Compiling nibble_vec v0.1.0
   Compiling form_urlencoded v1.0.1
   Compiling addr2line v0.17.0
   Compiling deflate v1.0.0
   Compiling clap_lex v0.2.4
   Compiling yaml-rust v0.4.5
   Compiling gif v0.11.4
   Compiling tracing v0.1.36
   Compiling radix_trie v0.2.1
   Compiling http-body v0.4.5
   Compiling unicode-normalization v0.1.21
   Compiling aho-corasick v0.7.19
   Compiling object v0.29.0
   Compiling want v0.3.0
   Compiling crossbeam-channel v0.5.6
   Compiling flate2 v1.0.24
   Compiling png v0.17.5
   Compiling quote v0.6.13
   Compiling num_cpus v1.13.1
   Compiling atty v0.2.14
   Compiling mio v0.8.4
   Compiling socket2 v0.4.6
   Compiling getrandom v0.2.7
   Compiling iovec v0.1.4
   Compiling net2 v0.2.37
   Compiling terminal_size v0.1.17
   Compiling dirs-sys-next v0.1.2
   Compiling dirs-sys v0.3.7
   Compiling signal-hook-registry v1.4.0
   Compiling nix v0.23.1
   Compiling spin v0.9.4
   Compiling idna v0.2.3
   Compiling regex v1.6.0
   Compiling cexpr v0.3.6
   Compiling clap v2.34.0
   Compiling clap v3.2.17
   Compiling threadpool v1.8.1
   Compiling nanorand v0.7.0
   Compiling console v0.15.1
   Compiling mio v0.6.23
   Compiling dirs-next v2.0.0
   Compiling dirs v4.0.0
   Compiling crossbeam-deque v0.8.2
   Compiling url v2.2.2
   Compiling fd-lock v3.0.6
   Compiling env_logger v0.6.2
   Compiling indicatif v0.15.0
   Compiling mio-uds v0.6.8
   Compiling rustyline v9.1.2
   Compiling failure v0.1.8
   Compiling tokio-util v0.7.3
   Compiling which v2.0.1
   Compiling futures-macro v0.3.23
   Compiling openssl-macros v0.1.0
   Compiling pin-project-internal v1.0.12
   Compiling tokio-macros v0.2.6
   Compiling jpeg-decoder v0.2.6
   Compiling tiff v0.7.3
   Compiling tokio v0.2.25
   Compiling pin-project v1.0.12
   Compiling flume v0.10.14
   Compiling exr v1.5.0
   Compiling tokio-native-tls v0.3.0
   Compiling h2 v0.3.14
   Compiling futures-executor v0.3.23
   Compiling discid-sys v0.4.0
   Compiling futures v0.3.23
   Compiling image v0.24.3
   Compiling hyper v0.14.20
   Compiling discid v0.4.4
   Compiling serde_urlencoded v0.7.1
   Compiling serde_yaml v0.8.26
   Compiling hyper-tls v0.5.0
   Compiling reqwest v0.11.11
   Compiling rdhist v0.1.0 (/home/user/projets/RecetteDHistoire/rdhist)
    Finished dev [unoptimized + debuginfo] target(s) in 53.79s

Ça fait un beau paquet de dépendances ! Mais ça se compile sans problème, et c’est même une des grandes qualité du langage : son outil de compilation et de gestion des bibliothèque.

Sur les 191 paquets utilisés nous avons :

  • 131 en version 0.x.x
  • 47 en version 1.x.x
  • 7 en version 2.x.x
  • 2 en version 3.x.x
  • 2 en version 4.x.x
  • 1 en version 6.x.x
  • 1 en version 9.x.x

Je ne sais pas si la version des dépendances est un bon indicateur de stabilité d’un langage. Mais il est indubitablement un indicateur de sa jeunesse à mon avis.

Prise en main du module ESP32-WROVER-E

Le module ESP32-WROVER-E est un module Wifi de chez Espressif à base de microcontrôleur Xtensa ® 32-bit LX6. On le trouve au tarif très compétitif d’environ 3.5€ chez les distributeurs habituels.

On trouve un kit de développement pour une grosse dizaine d’euros sur aliexpress: Le ESP32_DevKitc_V4. Et c’est le module que je viens de recevoir ce matin dans ma boite aux lettres.

Le module ESP32_WROVER_E et sa carte de développement ESP32_DevKitc_V4

La commande de ce kit n’est pas due au hasard, l’ESP32-WROVER-E est le module utilisé au cœur de la boite à histoires de France-inter/Bayard-Press : Merlin.

Branchement

Espressif fourni un tutoriel sur son wiki pour la prise en main du ESP32-DevKitC V4.

La photo officiel du kit de développement sur le wiki d’espressif

Au branchement de l’USB, un convertisseur USB-UART (cp210x) est détecté :

$ dmesg
[1174313.734558] usb 1-1.1.2: new full-speed USB device number 118 using xhci_hcd
[1174313.836871] usb 1-1.1.2: New USB device found, idVendor=10c4, idProduct=ea60, bcdDevice= 1.00
[1174313.836876] usb 1-1.1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[1174313.836878] usb 1-1.1.2: Product: CP2102N USB to UART Bridge Controller
[1174313.836880] usb 1-1.1.2: Manufacturer: Silicon Labs
[1174313.836882] usb 1-1.1.2: SerialNumber: 0ae36f4e9dfbeb11b9dfaef7c6d924ec
[1174313.843495] cp210x 1-1.1.2:1.0: cp210x converter detected
[1174313.846185] usb 1-1.1.2: cp210x converter now attached to ttyUSB0

Si on connecte un terminal au port série ttyUSB0 en 115200 bauds on obtient une suite de message qui se déroule indéfiniment :

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fffeba4,len:4
load:0x4009f000,len:3248
entry 0x4009f574
�OHAI�ets Jul 29 2019 12:21:46

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fffeba4,len:4
load:0x4009f000,len:3248
entry 0x4009f574
�OHAI�ets Jul 29 2019 12:21:46

Un appui sur le bouton «boot» provoque l’arrêt du défilement des message, sans doute dans l’attente du firmware qui doit passer par l’uart :

rst:0x10 (RTCWDT_RTC_RESET),boot:0x3 (DOWNLOAD_BOOT(UART0/UART1/SDIO_REI_REO_V2))
waiting for download

L’environnement de développement

Espressif propose un environnement de développement open source nommé ESP-IDF. Le guide d’installation officiel est suffisamment complet pour ne pas avoir à le décrire ici. Une fois que c’est installé, compilé et exporté on peut se focaliser sur le projet d’exemple.

Une fois le programme «hello world» d’exemple compilé, il suffit de lancer le flashage avec :

$ idf.py -p /dev/ttyUSB0 flash
Executing action: flash
Running ninja in directory /opt/esp/hello_world/build
Executing "ninja flash"...
[1/5] cd /opt/esp/hello_world/build/esp-idf/esptool_py && /home/fabien/.espressif/python_env/idf5.0_py3....opt/esp/hello_world/build/partition_table/partition-table.bin /opt/esp/hello_world/build/hello_world.bin
hello_world.bin binary size 0x298e0 bytes. Smallest app partition is 0x100000 bytes. 0xd6720 bytes (84%) free.
[2/5] Performing build step for 'bootloader'
[1/1] cd /opt/esp/hello_world/build/bootloader/esp-idf/esptool_py && /home/fabien/.espressif/python_env/idf5.0_py3.7_env/bin/python /opt/esp/esp-idf/components/partition_table/check_sizes.py --offset 0x8000 bootloader 0x1000 /opt/esp/hello_world/build/bootloader/bootloader.bin
Bootloader binary size 0x6350 bytes. 0xcb0 bytes (11%) free.
[2/3] cd /opt/esp/esp-idf/components/esptool_py && /usr/local/bin/cmake -D IDF_PATH=/opt/esp/esp-idf -D ...ING_DIRECTORY=/opt/esp/hello_world/build -P /opt/esp/esp-idf/components/esptool_py/run_serial_tool.cmake
esptool.py esp32 -p /dev/ttyUSB0 -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x1000 bootloader/bootloader.bin 0x10000 hello_world.bin 0x8000 partition_table/partition-table.bin
esptool.py v3.3-dev
Serial port /dev/ttyUSB0
Connecting.....
Chip is ESP32-D0WD-V3 (revision 3)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 34:94:54:b7:31:e0
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
Configuring flash size...
Flash will be erased from 0x00001000 to 0x00007fff...
Flash will be erased from 0x00010000 to 0x00039fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Compressed 25424 bytes to 15896...
Writing at 0x00001000... (100 %)
Wrote 25424 bytes (15896 compressed) at 0x00001000 in 0.7 seconds (effective 282.8 kbit/s)...
Hash of data verified.
Compressed 170208 bytes to 89791...
Writing at 0x00010000... (16 %)
Writing at 0x0001b0a3... (33 %)
Writing at 0x00020836... (50 %)
Writing at 0x00026023... (66 %)
Writing at 0x0002e652... (83 %)
Writing at 0x00036a08... (100 %)
Wrote 170208 bytes (89791 compressed) at 0x00010000 in 2.3 seconds (effective 581.7 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 103...
Writing at 0x00008000... (100 %)
Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.1 seconds (effective 479.6 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...
Done

Puis de se connecter au terminal uart avec screen pour obtenir la sortie fonctionnelle du programme :

$ screen /dev/ttyUSB0 115200
...
Restarting now.
ets Jul 29 2019 12:21:46

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:6736
load:0x40078000,len:14808
load:0x40080400,len:3792
entry 0x40080694
I (27) boot: ESP-IDF v5.0-dev-810-gb886dc6998 2nd stage bootloader
I (27) boot: compile time 21:20:00
I (27) boot: chip revision: 3
I (32) boot_comm: chip revision: 3, min. bootloader chip revision: 0
I (39) boot.esp32: SPI Speed      : 40MHz
I (43) boot.esp32: SPI Mode       : DIO
I (48) boot.esp32: SPI Flash Size : 2MB
I (52) boot: Enabling RNG early entropy source...
I (58) boot: Partition Table:
I (61) boot: ## Label            Usage          Type ST Offset   Length
I (69) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (76) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (84) boot:  2 factory          factory app      00 00 00010000 00100000
I (91) boot: End of partition table
I (95) boot_comm: chip revision: 3, min. application chip revision: 0
I (102) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=07824h ( 30756) map
I (122) esp_image: segment 1: paddr=0001784c vaddr=3ffb0000 size=023b0h (  9136) load
I (126) esp_image: segment 2: paddr=00019c04 vaddr=40080000 size=06414h ( 25620) load
I (141) esp_image: segment 3: paddr=00020020 vaddr=400d0020 size=14a4ch ( 84556) map
I (171) esp_image: segment 4: paddr=00034a74 vaddr=40086414 size=04e2ch ( 20012) load
I (180) esp_image: segment 5: paddr=000398a8 vaddr=50000000 size=00010h (    16) load
I (186) boot: Loaded app from partition at offset 0x10000
I (186) boot: Disabling RNG early entropy source...
I (200) cpu_start: Pro cpu up.
I (200) cpu_start: Starting app cpu, entry point is 0x40081004
I (187) cpu_start: App cpu up.
I (214) cpu_start: Pro cpu start user code
I (215) cpu_start: cpu freq: 160000000 Hz
I (215) cpu_start: Application information:
I (219) cpu_start: Project name:     hello_world
I (225) cpu_start: App version:      1
I (229) cpu_start: Compile time:     Dec 21 2021 21:19:52
I (235) cpu_start: ELF file SHA256:  81272dfce4dde882...
I (241) cpu_start: ESP-IDF:          v5.0-dev-810-gb886dc6998
I (248) heap_init: Initializing. RAM available for dynamic allocation:
I (255) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (261) heap_init: At 3FFB2CA0 len 0002D360 (180 KiB): DRAM
I (267) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (273) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (280) heap_init: At 4008B240 len 00014DC0 (83 KiB): IRAM
I (287) spi_flash: detected chip: generic
I (291) spi_flash: flash io: dio
W (295) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (309) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
Hello world!
This is esp32 chip with 2 CPU core(s), WiFi/BT/BLE, silicon revision 3, 2MB external flash
Minimum free heap size: 294172 bytes
Restarting in 10 seconds...
Restarting in 9 seconds...
Restarting in 8 seconds...

L’installation des outils et le flashage d’une première application «hello world» est incroyablement facile à mettre en œuvre. Il faudra voir à l’usage comment on peut décoder des MP3 et afficher des images provenant d’une carte SD.