Partage de valeurs entre deux ESP32 via la mémoire flash interne de l’un d’eux?

Bonjour la compagnie,

En clair, est-il possible qu’un ESP32 puisse lire des valeurs stockées dans la mémoire flash d’un autre ESP32 ?

Si non faut-il passer par une mémoire externe ? Si non quoi de mieux ?

En plus j’ai une contrainte de durée d’opération, je ne dispose que de 185 micro secondes maxi pour chaque lecture (entre deux interruptions externes), donc SPI obligatoire ? Si non là aussi quoi de mieux ?

Merci pour toute info.

pas vraiment une réponse, mais la première question que je me poserais c'est sur l'utilité des 2 ESP32. S'il n'y en avait qu'un seul, le problème ne se poserait pas :slight_smile:

Le but est de déclencher la production des étincelles de bougie d’allumage d’un moteur à essence (trois étincelles par tour).

L’un des ESP32 collecte des infos extérieures : température, dépression, hygrométrie, et vitesse de rotation d’une roue dentée (roue phonique 36-1).

Chaque dent de la roue passe devant un capteur à effet Hall qui produit une interruption du croquis. Donc 35 fois par tour, soit, au plus court, toutes les 185 micro secondes.

L’ESP32 calcule le meilleur moment pour chaque étincelle en fonction des valeurs extérieures collectées entre les interruptions.

L’ensemble devrait suffire au fonctionnement mais occupe l’ESP32 quasiment à plein temps, en tout cas beaucoup trop pour envisager de le faire piloter aussi un affichage des valeurs collectées et calculées, via écran ou smartphone par wifi.

D’où l’idée de déléguer l’affichage à un deuxième ESP32.

Celui-ci pourrait aussi collecter les infos extérieures en parallèle du premier et mais je trouve ça un peu lourd de doubler les acquisitions, une fois pour le calcul, une deuxième fois pour l’affichage.

D’autant que les acquisitions utilisent les convertisseurs AN intégrés, qui, même corrigés chacun par une table (LUT) renvoient des valeurs assez différentes…

Plutôt que de doubler les mesures faites par le premier ESP32, serait-il possible de les stocker dans une mémoire (interne ou externe) pour les mettre à disposition d’un deuxième ESP32 gérant l’affichage ?

Si non mieux ?

Je n'ai jamais exploré le partage de puces mémoire mais ça sent la contention lecture / écriture... Je me dirigerais donc plutôt vers une liaison série sur un bus par exemple en SPI

Je ne sais pas combien d'octets représentent ces valeurs collectées mais je suppose que l'affichage n'a pas besoin d'être changé toutes les 185 micro secondes :slight_smile: donc j'aurais une sorte de process asynchrone

  • vous collectez les données
  • vous figez une copie à transmettre dans un buffer
  • à chaque tranche de 185µs dispo, vous envoyez 32 bits (lire plus de truc sur le SPI ici)

imaginons que le payload total soit de 1Ko, ça veut dire qu'il vous faut 256 cycles pour envoyer les données.
ça veut dire que le second ESP dispose de toutes les données toutes les 50ms environ... ça vous permet quasiment un rafraichissement de l'affichage à 20Hz - donc on a sans doute pas mal de marge

Bonsoir

l'I2S parait également un moyen pour approprié transfert rapide pour transférer rapidement un paquet de donnés d'un ESP32 à un autre en bénéficiant du hardware I2S + DMA de cette puce, une fois configuré l'I2S de part et d'autre il me semble qu'il y aurait juste un buffer en RAM à gérer dans les 2 codes

Merci pour la confirmation que le SPI est le meilleur choix.

Pour l’instant je souhaite afficher autour de 6 valeurs de 1 ou 2 octets maxi chacune (alerte de cliquetis, régime moteur, degrés d’avance/PMH, et dépression, température et hygrométrie à l’admission).

L’affichage n’est pas indispensable mais il permet toutefois d’attester du bon fonctionnement des capteurs et du calculateur. Donc un rafraichissement autour de 2 Hz me suffit amplement (voire 4 Hz pour le cliquetis).

J’ai déjà utilisé le SPI pour communiquer avec quelques capteurs tout prêts (carte TF, BME280…) mais je n’ai aucune expérience d’une communication à construire sur mesure.

Merci pour le lien, toute aide est la bienvenue.

Merci al1fch pour la proposition, je débute, je ne connais pas I2S mais je vais chercher.

il existe encore une autre solution (du moins sur le papier) avec l'I2C en multi-maître pour accéder à une RAM I2C commune.

Avec une FRAM I2C (ferromagnétique donc non volatile), les 2 ESP32C pourraient avoir en prime un pot commu de paramètres (existe également en SPI)

(l'I2C en multimaïtre est il viable actuellement avec un ESP32 sous IDE Arduino avec Wire ? ... il semble que non...)

https://www.i2c-bus.org/multimaster/
https://github.com/esp8266/Arduino/issues/5645

a la réflexion i me semble que le SPI , simple à mettre en oeuvre , rend très accessible la solution de la RAM partagée entre 2 ESP32 (pas besoin de RAM à double port)

l'ESP32 s'assurant au préalable que CS est à 1 (=ressource dispoinible) avant de mettre CS à 0 puis accéder en lecture/écriture à la RAM commune

Nécessite un peu de travail côté logiciel et/ou matériel (selon les préférences !) pour pouvoir mettre en // les MISO, MOSI, SK et CS des 2ESP32

FRAM SPI
PSRAM SPI.......
.

Effectivement je n’y avais pas pensé car jamais utilisé directement

Vu le volume de données cela dit et les contraintes temporelles de rafraîchissement n’importe quelle solution asynchrone fonctionnerait y compris le bon vieux Serial (UART) - je favoriserais la simplicité de programmation : vous définissez un format de message et vous envoyez un seul octet à chaque opportunité. Vous pouvez vous payer le luxe d’avoir une trame bien structurée pour les messages

Ok pour passer par une mémoire externe.
La FRAM semble un bon choix compte tenu de sa durée de vie.

Autant je me sens apte à raccorder d'une FRAM en SPI à deux ESP32, autant je saurai pas lire et écrire dessus, même si je me contente de valeurs d'un octet.

Comment connaitre les adresses des cellules de mémoire pour y écrire ?
Quelles instructions utiliser pour écrire et lire des valeurs dans cette mémoire ?

Merci pour toute aide.

Adafruit a une FRAM SPI (plus rapide qu'en I2C) et une bibliothèque associée

Cette option nécessitera cependant une moyen de synchro entre les 2 cartes pour savoir quand les données sont accessibles (et ne pas essayer de lire les données pendant que l'autre ESP les met à jour)

compter aussi au minimum 1µs par octet écrit


personnellement je ne pense pas que ce soit la bonne approche. Etant donné qu'il n'y a aucun besoin de temps réel côté réception, J'utiliserais la liaison série UART et j'enverrais les données que de temps en temps (une fois toutes les secondes par exemple) et je laisserai le hardware gérer cela en tâche de fond.

L'ESP émetteur pourrait soit calculer une valeur moyenne sur la seconde, soit juste envoyer les dernières mesures

comme ça pas de hardware supplémentaire, juste Tx1 ➜ Rx1 et les GND à connecter

Merci pour votre avis, effectivement l'UART paraît simple et appropriée dans ce cas.
Je n'y connais rien mais je vais creuser.

Connaitriez vous les meilleurs liens pour apprendre ce mode de communication ?

J'ai un petit tuto sur le port série (ou pour gérer un flux asynchrone genre keypad) vous pouvez jeter un oeil à ce sujet ici

Je viens de mesurer avec le décimal "valeur" les durées de Serial.print(valeur) et de Serial.print(String(valeur)), c'est respectivement 96 et 60 microsecondes sur le moniteur série à 115200 baud.

Reste à tester entre deux ESP32 pour voir si c'est confirmé.

J'utiliserai une broche commune entre les deux ESP32 pour le handshake.

Merci bien pour l'idée et les liens.

comment avez vous mesuré ?

votre valeur n'est que d'un seul caractère ? 115200 bauds vous émettez ~11 520 caractères par seconde, soit ~87µs par caractère.

pour mesurer il faut mettre un flush() à la suite du print car la classe travaille en asynchrone, par interruptions. le flush va lui dire d'attendre que tout soit émis.

Mais en pratique dans votre code il ne faut pas faire flush et bloquer le programme, il faut juste le laisser émettre ses octets de temps en temps, l'interruption sera courte (charger le bon octet dans le registre, ensuite c'est du hard qui travaille en parallèle de votre code pour émettre les bits)

plus le débit en baud sera élevé, plus les interruptions pour mettre le nouvel octet dans le registre seront rapprochés, donc il faut trouver le juste milieu pour ne pas perturber trop une session de mesures mais étaler l'envoi sur plusieurs sessions.

Je viens de mesurer avec le décimal "valeur" les durées de Serial.print(valeur) et de Serial.print(String(valeur)), c'est respectivement 96 et 60 microsecondes sur le moniteur série à 115200 baud.

....sans compter que l'UART des ESP32 peut fonctionner à des débits bien supérieurs à 115200 bauds
(> 1.000.000 bauds)

Faut il comprendre que la valeur envoyée à l'affichage par Serial.print(valeur) n'emprunte pas le chemin que la même valeur passant par Tx ?
L'une est transmise et affichée en une fois alors que l'autre est envoyée par morceau au buffer ?

Je ne comprends pas ce que vous voulez dire…

Quand vous faites Serial.print(), ce que vous avez demandé d’afficher (pour une variable numérique ce sera sa représentation en ASCII) est copié entièrement s’il y a la place dans un buffer interne à l’instance Serial et l’appel à print est terminé, le code poursuit son chemin à l’instruction suivant le print.

En tâche de fond, la classe HardwareSerial utilise les capacités matérielles UART pour émettre tous les octets du buffer.

Cela fonctionne par interruption du code principal: un octet est mis dans le registre de transmission de l’UART, qui se charge en parallèle de votre code d’émettre chacun des bits nécessaires au débit voulu.

Quand les 8 bits de l’octet ont été transmis (et les bits de contrôle éventuellement) une interruption est émise par l’UART pour dire, "c’est bon l’octet est envoyé". La classe HardwareSerial attrape cette interruption, regarde s’il y a encore des octets à émettre dans le buffer de sortie, et si oui prend le prochain et le met dans le registre de transmission de l’UART et rend la main.

Le process se répète jusqu’à ce qu’il n’y ait plus rien dans le buffer de sortie.

Donc si vous mesurez la durée de juste l’instruction print(), vous n’avez pas mesuré le temps nécessaire à toute l’émission mais juste le transfert des données dans le buffer et la mise du premier octet dans le registre de transmission de l’UART s’il n’y en avait pas.

Merci beaucoup de donner de votre temps pour expliquer.
J'ai bien compris et ça m'a donné l'idée d'une l'illustration où l'on voit bien ce vous expliquez. Merci encore.

Le croquis :

digitalWrite(2, HIGH);
 delayMicroseconds(4);
 digitalWrite(2, LOW);

 Serial.println("a");

 digitalWrite(2, HIGH);
 delayMicroseconds(4);
 digitalWrite(2, LOW);

L'analyseur logique (Tx et GPIO 2)

1 Like