Les cartes basées sur un microcontrôleur ESP 32

Ou comment bénéficier d'une RAM de 8Mo... et de nombreuses interfaces avec un CPU cadensé à 240MHz

1 Au delà des AVR et Arduino...

Cela faisait quelques années que nous utilisions les microcontrôleurs AVR soit directement soit implantés sur des cartes Arduino. Nous avions rapidement choisi les modèles les plus puissants de la gamme que sont les ATmega2560, rendus nécessaires par la gestion d'un affichage TFT. Toutefois lors des mes dernières réalisations (Géné 4GHz, Station météo) j'ai dû sévèrement optimiser le code afin que son exécution puisse tenir dans le 8ko de la RAM disponible. Il devenait donc urgent de se tourner vers des solutions plus performantes.

C'est ainsi que je me suis intéressé aux microcontrôleurs ESP 32 qui offrent non plus 8 kilo-octet de RAM mais... 8 Méga-octet ! Quant à la fréquence on passe de 16 MHz à... 240 MHz, sans compter les nombreuses interfaces (des cartes) disponibles Bluetooth, SPI, WIFI, TFcard, ADC, DAC, voir même carte SIM (sur certains modèles)... Le tout avec une consommation électrique bien plus faible que celles des Raspberry Pi (que j'utilise également et que j'apprécie tout particulièrement!)

Et puisque j'ai commencé les comparaisons, en voici d'autres :
  • processeur double coeurs 32 bits (au lieu d'un simple coeur 8 bits)
  • support Wi-Fi et Bluetooth intégrés sur la puce
  • coprocesseur ultra basse consommation
  • Cryptographie (multiples protocoles)
Il sont programmables par l'IDE Arduino, beaucoup de bibliotheques Arduino ont été transposées pour les ESP32... Et l'on peut très facilement utiliser Geany comme éditeur de code.

Alors certes, ces formidables bestioles donnent aussi la possibilité d'exister à la folie IoT 'Internet of Things' (et en sont aussi sans doute la conséquence) qui enflamme nos dirigeants téléguidés par le côté obscur de la force ! Alors que les choses soient bien claires : Je ne fais pas l'éloge de l'Internet des objets, qui permettra de m'appeler sur mon smartphone pour me signaler qu'il n'y a plus de lait dans mon frigo ! (Et le signalera d'ailleurs peut-être au Ministère de la Consommation Obligatoire). Je vais me contenter d'apprivoiser ces bidules programmables pour en faire des choses sympathiques.

J'ai oublié de mentionner la carte Arduino Due : elle est basée sur un microcontrôleur ARM 32bits AT91SAM3X8E tournant à 84MHz et disposant de 96kB de SRAM. Ce qui élargissait déjà grandement le champs de possibilités. Je vous laisse comparer.

Bon pour l'instant je vous laisse, je vais aller vérifier s'il reste suffisamment de lait dans le frigo !

2 La consommation électrique des ESP32

13 dec 2020 :
J'ai commandé hier une carte TTGO (de chez LILYGO) T7 V1.5 Mini32 ESP32-WROVER-B pour la modique somme de 6,32€ et dont voici l'image ->

J'ai choisi cette carte parmi des dizaines disponibles parce que le microprocesseur est du type ESP32 WROVER (et pas WROOM), la différence entre les deux etant que les WROWER disposent de 2 à 8Mo de mémoire PSRAM, au contraire des WROOM qui en sont dépourvus.

La consommation de cette carte est annoncée égale à 30mA en fonctionnement et 10uA (dix micro-ampère) en mode sleep ! A comparer aux 2A des Raspberry Pi 4. Toutefois gardons à l'esprit que les Raspberry ne tournent pas à 250MHz mais à 1GHz et sont capables de décoder de la vidéo en Full HD 1080p voire 4K. Donc il s'agit de deux domaines d'applications bien distincts, un fonctionnement en continu alimenté par batterie LiPo étant envisageable pour les ESP32.

Quant-à moi, ma première réalisation avec cette carte ESP sera de faire une horloge avec afficheur E-Ink (e-paper). Cela fera l'objet d'un nouvel article (le temps de recevoir ces composants). A bientôt donc...

3 Utiliser l'IDE Arduino pour programmer les ESP

.

Il faut ajouter le framework ESP-IDF pour ESP32 :
  • Lancer l'IDE Arduino
  • Aller dans Fichier/Préférences
  • Ajouter l'URL de gestionnaire de cartes supplémentaires :
https://dl.espressif.com/dl/package_esp32_index.json

4 INO - Gestionnaire de cartes

Ensuite, aller dans Outils/Type de carte/Gestionnaire de cartes

5 INO - paquet esp32

Puis installer le paquet esp32 qui comprend en fait plusieurs cartes

6 nombreuses cartes utilisant l'ESP32...

On a maintenant accès aux nombreuses cartes utilisant l'ESP32.
On peut donc programmer ces cartes directement dans l'EDI Arduino.
Mais on peut aussi utiliser un éditeur externe (comme Geany) !

7 Utiliser Geany comme éditeur

Commencer par faire fonctionner la programmation avec l'EDI Arduino, comme expliqué ci-dessus. Puis, dans cet EDI, cocher "utiliser un éditeur externe" dans Préférences/Paramètres.

Dès lors, le fichier de code source préalablement créé par l'EDI Arduino peut être travaillé en externe par Geany (ou tout autre éditeur), puis sauvegardé par cet éditeur externe. On constate alors que l'affichage dans l'EDI Arduino est automatiquement mis à jour. Et cet EDI Arduino nous sert alors "juste" à compiler le code et à le downloader sur la carte ESP32.

8 Premiers tests

J'ai également commandé un LilyGo_T5 qui comprend un ESP32 et un petit afficheur e-paper de 2.3". Je viens de le recevoir, je vais tester...

9 Programmation OK

29 dec 2020 :
programmation avec l'IDE Arduino et Geany comme éditeur de code source.
Voici un premier résultat ; Je vais publier le code source et tous les renseignements utiles dès que je trouve le temps de le faire.

10 Le code source en C++

CODE SOURCE en C++
  1.  
  2. //** TEST_LilyGo_T5 **/
  3.  
  4.  
  5. #define version "2.0"
  6.  
  7. //décommenter une des (ou les deux) lignes suivantes pour afficher des tests d'affichages
  8. //#define IMAGE_BMP
  9. //#define GROS_CARACT_1
  10.  
  11.  
  12. #include <stdint.h>
  13. #include <GxEPD.h>
  14. #include "SD.h"
  15. #include "SPI.h"
  16.  
  17.  
  18. //! There are three versions of the 2.13 screen,
  19. // if you are not sure which version, please test each one,
  20. // if it is successful then it belongs to the model of the file name
  21.  
  22. //#include <GxGDE0213B1/GxGDE0213B1.h> // 2.13" b/w
  23. //#include <GxGDEH0213B72/GxGDEH0213B72.h> // 2.13" b/w new panel
  24. #include <GxGDEH0213B73/GxGDEH0213B73.h> // 2.13" b/w newer panel
  25.  
  26.  
  27. /**
  28. REMARQUE :
  29. La page html de conversion "image2cpp" est ici : https://javl.github.io/image2cpp/
  30. Elle fonctionne également en local
  31. */
  32. #include "bmp/bmp_Skippy.c"
  33.  
  34. #include "chiffres/60_75/chiffres60_75.c"
  35.  
  36.  
  37.  
  38. // FreeFonts from Adafruit_GFX
  39. #include <Fonts/FreeMonoBold9pt7b.h>
  40. #include <Fonts/FreeMonoBold12pt7b.h>
  41. #include <Fonts/FreeMonoBold18pt7b.h>
  42. #include <Fonts/FreeMonoBold24pt7b.h>
  43.  
  44.  
  45. #include <GxIO/GxIO_SPI/GxIO_SPI.h>
  46. #include <GxIO/GxIO.h>
  47.  
  48. #define SPI_MOSI 23
  49. #define SPI_MISO -1
  50. #define SPI_CLK 18
  51.  
  52. #define ELINK_SS 5
  53. #define ELINK_BUSY 4
  54. #define ELINK_RESET 16
  55. #define ELINK_DC 17
  56.  
  57. #define SDCARD_SS 13
  58. #define SDCARD_CLK 14
  59. #define SDCARD_MOSI 15
  60. #define SDCARD_MISO 2
  61.  
  62. #define BUTTON_PIN 39
  63.  
  64.  
  65. uint8_t B=4; // bouton
  66. const uint32_t partial_update_period_s = 1;
  67. const uint32_t full_update_period_s = 6 * 60 * 60;
  68.  
  69. uint32_t start_time;
  70. uint32_t next_time;
  71. uint32_t previous_time;
  72. uint32_t previous_full_update;
  73.  
  74. uint32_t total_seconds = 0;
  75. uint32_t seconds, minutes, hours;
  76.  
  77.  
  78.  
  79.  
  80. GxIO_Class io(SPI, /*CS=5*/ ELINK_SS, /*DC=*/ ELINK_DC, /*RST=*/ ELINK_RESET);
  81. GxEPD_Class display(io, /*RST=*/ ELINK_RESET, /*BUSY=*/ ELINK_BUSY);
  82.  
  83. SPIClass sdSPI(VSPI);
  84.  
  85.  
  86. const char *skuNum = "SKU:H239";
  87. bool sdOK = false;
  88. int startX = 40, startY = 10;
  89.  
  90.  
  91.  
  92.  
  93. void setup()
  94. {
  95. pinMode(BUTTON_PIN, INPUT);
  96.  
  97. Serial.begin(115200);
  98. Serial.println();
  99. Serial.println("setup");
  100. SPI.begin(SPI_CLK, SPI_MISO, SPI_MOSI, ELINK_SS);
  101.  
  102.  
  103. if (sdOK)
  104. {
  105. uint32_t cardSize = SD.cardSize() / (1024 * 1024);
  106. display.println("SDCard:" + String(cardSize) + " MB");
  107. } else { display.println("SDCard None"); }
  108.  
  109. start_time = next_time = previous_time = previous_full_update = millis();
  110.  
  111. display.init();
  112.  
  113. display.setRotation(1);
  114. display.fillScreen(GxEPD_WHITE);
  115. display.setTextColor(GxEPD_BLACK);
  116. display.setFont(&FreeMonoBold12pt7b);
  117. display.setCursor(0, 0);
  118.  
  119. sdSPI.begin(SDCARD_CLK, SDCARD_MISO, SDCARD_MOSI, SDCARD_SS);
  120.  
  121. if (!SD.begin(SDCARD_SS, sdSPI)) { sdOK = false; } else { sdOK = true; }
  122.  
  123. esp_sleep_enable_ext0_wakeup((gpio_num_t)BUTTON_PIN, LOW);
  124.  
  125. display.fillScreen(GxEPD_WHITE);
  126. // display.eraseDisplay();
  127. display.update();
  128.  
  129.  
  130.  
  131. // void drawBitmap(const uint8_t *bitmap, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color, int16_t mode = bm_normal);
  132.  
  133. #if defined(IMAGE_BMP)
  134. display.drawBitmap(bmp_Skippy_bits, 0, 0, bmp_Skippy_W, bmp_Skippy_H, GxEPD_BLACK);
  135.  
  136. display.setTextColor(GxEPD_BLACK);
  137.  
  138. display.setFont(&FreeMonoBold9pt7b);
  139. display.setCursor(display.width() - display.width() / 2, display.height() -100);
  140. display.println("Skippy");
  141.  
  142. display.setCursor(display.width() / 2 -30, display.height() -10);
  143. display.setFont(&FreeMonoBold24pt7b);
  144. display.setCursor(display.width() - display.width() / 2 -30, display.height() -60);
  145. display.println("18:45");
  146.  
  147. display.update();
  148. delay(4000);
  149.  
  150. display.fillScreen(GxEPD_WHITE);
  151. display.update();
  152. #endif
  153.  
  154.  
  155. #if defined(GROS_CARACT_1)
  156. display.setFont(&FreeMonoBold24pt7b);
  157. display.setTextSize(2);
  158.  
  159.  
  160. display.setCursor(0, 90);
  161. display.println("18");
  162.  
  163. display.setCursor(120, 90);
  164. display.println("45");
  165.  
  166. display.setCursor(100, 70);
  167. display.setTextSize(1);
  168. display.println(":");
  169.  
  170. display.update();
  171. delay(1000);
  172. #endif
  173.  
  174. uint16_t x0=0;
  175. uint16_t y0=20;
  176. uint16_t w0=60;
  177. uint16_t h0=75;
  178.  
  179. display.fillScreen(GxEPD_WHITE);
  180.  
  181.  
  182. display.setFont(&FreeMonoBold12pt7b);
  183. display.setTextSize(1);
  184.  
  185. }
  186.  
  187.  
  188. void print_big_chiffre(uint8_t nb, uint16_t x0 ,uint16_t y0)
  189. {
  190. uint16_t w0=60;
  191. uint16_t h0=75;
  192.  
  193. switch (nb)
  194. {
  195. case 0: {display.drawBitmap(chiffre0, x0, y0, w0, h0, GxEPD_BLACK); } break;
  196. case 1: {display.drawBitmap(chiffre1, x0, y0, w0, h0, GxEPD_BLACK); } break;
  197. case 2: {display.drawBitmap(chiffre2, x0, y0, w0, h0, GxEPD_BLACK); } break;
  198. case 3: {display.drawBitmap(chiffre3, x0, y0, w0, h0, GxEPD_BLACK); } break;
  199. case 4: {display.drawBitmap(chiffre4, x0, y0, w0, h0, GxEPD_BLACK); } break;
  200. case 5: {display.drawBitmap(chiffre5, x0, y0, w0, h0, GxEPD_BLACK); } break;
  201. case 6: {display.drawBitmap(chiffre6, x0, y0, w0, h0, GxEPD_BLACK); } break;
  202. case 7: {display.drawBitmap(chiffre7, x0, y0, w0, h0, GxEPD_BLACK); } break;
  203. case 8: {display.drawBitmap(chiffre8, x0, y0, w0, h0, GxEPD_BLACK); } break;
  204. case 9: {display.drawBitmap(chiffre9, x0, y0, w0, h0, GxEPD_BLACK); } break;
  205. }
  206. }
  207.  
  208.  
  209.  
  210. void Update_box()
  211. {
  212. uint16_t box_x = 0;
  213. uint16_t box_y = 20;
  214. uint16_t box_w = 245;
  215. uint16_t box_h = 80;
  216. uint16_t cursor_y = box_y;
  217. display.fillRect(box_x, box_y, box_w, box_h, GxEPD_WHITE);
  218. display.setCursor(box_x, cursor_y);
  219.  
  220. uint8_t x0= box_x;
  221.  
  222. /*
  223. uint8_t heures_unites;
  224. uint8_t heures_dizaines;
  225. heures_unites = hours %10;
  226. heures_dizaines = hours /10;
  227. print_big_chiffre(heures_dizaines, x0, cursor_y);
  228. print_big_chiffre(heures_unites, x0+60, cursor_y);
  229.  
  230. x0+=120;
  231. */
  232.  
  233. uint8_t minutes_unites;
  234. uint8_t minutes_dizaines;
  235. minutes_unites = minutes %10;
  236. minutes_dizaines = minutes /10;
  237. print_big_chiffre(minutes_dizaines, x0, cursor_y);
  238. print_big_chiffre(minutes_unites, x0+60, cursor_y);
  239.  
  240. display.setCursor(113, 65);
  241. display.setTextSize(2);
  242.  
  243. display.println(":");
  244.  
  245. x0+=130;
  246.  
  247. uint8_t secondes_unites;
  248. uint8_t secondes_dizaines;
  249. secondes_unites = seconds %10;
  250. secondes_dizaines = seconds /10;
  251. print_big_chiffre(secondes_dizaines, x0, cursor_y);
  252. print_big_chiffre(secondes_unites, x0+60, cursor_y);
  253.  
  254.  
  255. if (B==0)
  256. {
  257. display.setFont(&FreeMonoBold9pt7b);
  258. display.setTextSize(1);
  259. display.setCursor(box_x, box_y);
  260. display.println("sleeping");
  261. }
  262.  
  263.  
  264. display.updateWindow(box_x, box_y, box_w, box_h, true);
  265.  
  266. }
  267.  
  268.  
  269.  
  270. void loop()
  271. {
  272.  
  273. B = digitalRead(BUTTON_PIN);
  274.  
  275. uint32_t actual = millis();
  276. while (actual < next_time)
  277. {
  278. // the "BlinkWithoutDelay" method works also for overflowed millis
  279. if ((actual - previous_time) > (partial_update_period_s * 1000))
  280. {
  281. //Serial.print(actual - previous_time); Serial.print(" > "); Serial.println(partial_update_period_s * 1000);
  282. break;
  283. }
  284. delay(100);
  285. actual = millis();
  286. }
  287. //Serial.print("actual: "); Serial.print(actual); Serial.print(" previous: "); Serial.println(previous_time);
  288. if ((actual - previous_full_update) > full_update_period_s * 1000)
  289. {
  290. display.update();
  291. previous_full_update = actual;
  292. }
  293.  
  294. previous_time = actual;
  295. next_time += uint32_t(partial_update_period_s * 1000);
  296. total_seconds += partial_update_period_s;
  297. seconds = total_seconds % 60;
  298. minutes = (total_seconds / 60) % 60;
  299. hours = (total_seconds / 3600) % 24;
  300.  
  301.  
  302. Update_box();
  303.  
  304.  
  305. if (B == 0)
  306. {
  307. // goto sleep
  308. display.update();
  309. delay(1000);
  310. esp_deep_sleep_start();
  311.  
  312. while(1) {;}
  313. }
  314.  
  315.  
  316.  
  317. }
  318.  


11 Très gros caratères

Les polices (Fonts) les plus grosses présentes dans la library "FreeMonoBold24pt7b.h" sont des 24pt. Or je voulais utiliser des caractères encore plus gros (pour faire une horloge lisible dans toute la pièce). Et c'est tout à fait possible : il suffit d'utiliser la fonction setTextSize(2). Mais j'ai dû en plus tricher un peu afin que les ":" ne soient pas eux aussi très gros (et très larges), c'est à dire faire un "crénage", voir le code, et le résultat ci-contre.

Vu de près Il apparaît un crénelage assez prononcé, c'est normal parce que la fonction de mise à l'échelle ne change pas la résolution de la font, elle ne fait que dédoubler les pixels. L'avantage de la méthode c'est de ne pas occuper davantage de place en mémoire. Si l'on veut obtenir un rendu impeccable, il va falloir dénicher (ou écrire) une font plus grande. Ou alors zieuter du côté des polices vectorielles, c'est vrai qu'on a ici un microcontrôleur rapide !

12 Chiffres nets

01 janv 2021 :
Bonne année à tous !

J'ai codé une police de gros caractères, des chiffres uniquement, énormes et bien nets (60x75px): cette fois plus de crénelage. Maintenant tout est prêt pour créer notre horloge (j'attends juste de recevoir des modules RTC DS1307).

13 DOCUMENTS

Code source en C++ Autres fichiers :

14 -

Liens...

7038