Arduino Due et Flow control

Bonjour à vous!

Suite à cette discussion, j'ai besoin d'implémenter un contrôle de flux logiciel (Xon/Xoff) sur la liaison série d'une Arduino Due.
J'ai reçu la carte aujourd'hui, et ça avance. Mais je bute sur quelque chose que je ne comprends pas.

J'ai créé une classe qui permet de gérer très simplement un buffer circulaire, dont la taille est déterminée lors de la création de l'objet. Puisqu'il m'en faut dans mon projet, autant gagner du temps pour la suite.
Puis une classe liaison série qui est une version modifiée de la classe UART de l'Arduino Due, qui fait tout pareil que celle d'origine, mais ajoute en plus la possibilité de gérer le contrôle du flux. S'il est activé, à chaque réception de caractère la taille du buffer est testée, et si le seuil est dépassé un drapeau est levé afin que le caractère XOFF soit envoyé.
D'autre part, à chaque lecture d'un caractère la taille du buffer est testée également, pour envoyer XON si on passe sous le seuil correspondant.

Pour finir, j'ai créé une variante de la carte dans l'IDE, pour désactiver l'ISR lié à l'objet Serial dans el fichier variant.cpp, et ainsi pouvoir l'appliquer à celui que je veux utiliser.

En théorie c'est tout bon. En pratique, les tests que j'ai fait avec coolterm me laissent sur ma faim:
Si je désactive le flow control dans coolterm, et envoie un "gros" fichier (c'est à dire, quelques centaines de caractères), je vois apparaître à intervalle régulier les caractères correspondants (0x13 pour xoff et 0x11 pour xon). Sans surprise, puisque j'ai une pause dans ma boucle principale, et puisque le flow control est désactivé, je pers pas mal de données.
Maintenant, si j'active le flow control dans coolterm, je ne vois plus apparaître les caractères Xon et Xoff, ce qui semble normal puisque le programme doit les considérer comme des instructions, et non comme des données. Mais ça ne change rien, la totalité du fichier est envoyée sans les prendre en compte, et j'ai donc une perte de donnée là aussi.

Je joins les deux classes mentionnées ci-dessus, et le programme en question. Je joins également le fichier variant.cpp, pour la définition de l'ISR qui est désactivée.

Toute suggestion est bienvenue, je ne vois pas où peut être le problème.

Merci d'avance!

variant.cpp (22.7 KB)

circularbuffer.cpp (1.48 KB)

circularbuffer.h (1.2 KB)

serialdue.cpp (4.61 KB)

serialdue.h (2.96 KB)

SerialDue.ino (277 Bytes)

Bonjour,

je n'ai pas lu, mais quelques réflexions :

  • buffer circulaire, c'est un peu un pléonasme
  • si tu utilises xon xoff, il faut t'assurer de ne pas rencontrer accidentellement 17 ou 19 dans les bytes transmis, en d'autres termes, que le protocole utilisé soit 100% compatible avec cette gestion de flux
  • coolterm, que je ne connais pas, fonctionne sous windaube (ou autre OS ?). A la réception d'un xoff, l'OS cessera d'émettre les données ... quand ça lui plaira. Il faut donc prévoir une bonne réserve au niveau du buffer de l'arduino. Avec le Due ce n'est pas la RAM qui manque
  • un point de vue perso : les buffers c'est bien (j'en mets partout), le contrôle de flux je préfère éviter, en augmentant la profondeur du buffer et la réactivité du traitement

Merci pour les réponses!

Ok pour buffer circulaire. D'un certain point de vue, c'est un peu un pléonasme. :slight_smile:

En principe pas de Xon et Xoff dans les caractères transmis: Je génère les données moi-même, et ce n'est que du texte humainement lisible.

CoolTerm marche pas mal, et est assez souple. Il tourne aussi sur mac (et sur linux au prix de pas mal d'arrachage de cheveux pour trouver et installer les bonnes bibliothèques). Je ne pense pas (mais je conçois que cela puisse être ça) que sa réactivité puisse être en cause: je l'utilise pour piloter mes deux CNC, une détoureuse et un laser de 40W qui tournent sous TinyG et G2. Pas de problème, même avec des fichiers de plusieurs milliers de lignes.

Dans mon cas, le contrôle de flux est obligatoire (du moins il me semble): Je veux envoyer des images à la carte qui pilote un laser et ses deux galvos, se charge de gérer des calculs de trajectoire, de position et d'intensité du laser. Chaque pixel est transmis sous forme de coordonnée X23000Y5000L134M1S2000, ce qui fait beaucoup, beaucoup de données à traiter.
Et comme la vitesse de déplacement est commandée aussi, on peut très bien avoir une coordonnée qui implique un déplacement de 3ms, alors que la suivante va gérer un déplacement de 4 minutes.

J'avais commencé par implémenter un contrôle manuel, genre la carte envoie un caractère ou une chaîne dédiée quand elle veut des données. mais d'abord ça n'est pas forcément plus simple à gérer, et ensuite la bibliothèque avec laquelle je conçois le programme de l'autre coté implémente Xon et Xoff...

peux-tu mesurer le temps qui se passe entre l'envoi de xoff vers le pc et l'arrêt des émissions par ce dernier ?
Cela te permettra de connaître la marge de sécurité à ajouter au buffer, quelque chose comme 2temps / baud10 (le 2 étant à discuter)

Je génère les données moi-même

donc ce n'est pas la faute de l'UE si cela rate :slight_smile:
utilises-tu un langage de programmation, quel est l'utilité de Coolterm dans ce cas ?

ce n'est que du texte humainement lisible

ce qui est un grand avantage pour l'humain, notamment en phase de développement, mais pas pour la machine qui est dans ce cas contrainte à des calculs qu'elle, si elle pouvait s'exprimer, qualifierait d'inutile

trimarco232:
peux-tu mesurer le temps qui se passe entre l'envoi de xoff vers le pc et l'arrêt des émissions par ce dernier ?
Cela te permettra de connaître la marge de sécurité à ajouter au buffer, quelque chose comme 2temps / baud10 (le 2 étant à discuter)

donc ce n'est pas la faute de l'UE si cela rate :slight_smile:
utilises-tu un langage de programmation, quel est l'utilité de Coolterm dans ce cas ?

ce qui est un grand avantage pour l'humain, notamment en phase de développement, mais pas pour la machine qui est dans ce cas contrainte à des calculs qu'elle, si elle pouvait s'exprimer, qualifierait d'inutile

Pour le premier point, je ne sais pas. Mon problème est justement que le PC n'arrête visiblement rien du tout. Mais je vais essayer d'augmenter la taille du buffer, oui.

L'intérêt de coolterm dans mon cas, c'est de développer la partie du programme consacrée à la liaison UART avec un contrôle du flux. Quand je serai sûr que cela fonctionne je laisserai coolterm de coté pour revenir sur mon programme PC.

Pour le texte humainement lisible, en effet il faudra peut-être que je me penche sur la question. Mais l'un dans l'autre, si je mets au point un protocole pour gagner sur la taille des données envoyées, est-ce que je ne vais pas perdre de l'autre coté sur le parsage de ces données? De toutes façon ce point-là me parait secondaire compte tenu de ce qu'il y a déjà à faire sur ce projet.

je laisserai coolterm de coté

je te suggère de le faire de suite. S'il y a des problèmes cachés, autant en réduire au mieux les sources potentielles
quel langage de programmation utilises-tu côté PC ?

si je mets au point un protocole pour gagner sur la taille des données envoyées, est-ce que je ne vais pas perdre de l'autre coté sur le parsage de ces données

le but de ma remarque n'était pas de compacter les données mais justement d'en faciliter (accélérer) le décodage. As tu prévu la vérification de leur intégrité, ce qui me semble indispensable dans le domaine du make ?

Le langage coté PC est du C++, dans un environnement Qt, qui fournit la majorité des classes utilisées (et notamment QSerialPort pour la liaion UART).
Je veux bien laisser coolterm de coté, seulement là mon problème est d'avoir un flow control fonctionnel. Si je bascule sur le programme que j'écris, d'abord il faut que j'implémente une liaison série complète (alors que je n'ai besoin que d'envoyer, pas de réceptionner), ensuite il faut que je fasse tourner l'intégralité du programme coté Due. Ce qui rajoute un nombre conséquent de bugs possibles, sans savoir non plus s'ils seront imputables à mon implémentation du serial coté due, coté pc, ou encore autre chose...

Je ne m'y connais pas beaucoup en formatage de données, mais il me semble difficile de faire plus simple que ce que je fais, non? Mettre une lettre pour indiquer quelle valeur est transmise, puis la valeur, c'est dur de faire plus simple, non?
Pour la vérification de donnée, je n'en ai pas implémenter encore, non. Parce que puisque des données il en transite beaucoup, et que c'est clairement le poste goulet du projet, je préfère autant limiter les allers-retours.

Cela dit pour l'instant mon problème est la question du serial, le reste tournait bien sur Uno, il tournera bien sur Due. Hier j'ai fait des essais sur les ISR de timer, c'est presque plus simple que sur les AVR!

c'est dur de faire plus simple, non?

pas pour l'arduino, qui reçoit des lettres et doit les transformer en chiffres avant de pouvoir faire ses calculs. Bon, je ne vais pas insister pour le dixième de microseconde nécessaire à un switch case, mais je me méfie des bibliothèques arduino qui ont une tendance naturelle à sacrifier la vitesse d'exécution à la facilité de développement

j'ai fait des essais sur les ISR de timer, c'est presque plus simple que sur les AVR!

as-tu hyérarchisé les priorités pour ces ISR ?

Je me méfie aussi des bibliothèques arduino, pour la même raison. Quand je vois l'usine à gaz que sont par exemple les fonctions d'écriture, j'aime autant mettre à jour un registre si ça doit aller vite.

Dans le cas de ma liaison série, j'ai implémenté un parseur qui va à l'essentiel: trouver une lettre (variable), trouver un nombre (valeur), mettre les deux ensemble, basta.

Pour les ISR, non, je n'ai pas hierarchisé les priorités. D'abord je n'en ai que deux, l'un pour un timer qui gère la fréquence de mise à jour de la position, et l'autre pour la liaison série (deux dans le cas de l'Uno). Ensuite, la boucle principale, elle, est hiérarchisée: chaque fonction retourne un statut, et en fonction de ce statut la boucle redémarre, ou poursuit vers les fonctions de priorité moindre.

Pour information le code est là. (La branche Uno est à jour, et tourne sans problème autre que la rapidité d'exécution trop faible)

Cela dit, pour l'instant c'est le contrôle de flux qui me pose problème. Le reste fonctionnant sur l'Uno, fonctionnera sur la Due, à quelques détails près. Mais j'aimerais vraiment obtenir des résultats sur ce point précis.

Bon, va falloir que je te laisse travailler, tu as tout le temps, si tu le souhaites, de modifier ta façon de faire pour la suite.

Trimarco232, mon but n'était pas d'être sec dans ma réponse. Je suis bien sûr preneur de toute astuce, méthodologie, etc. qui me permettent d'arriver à mes fins. Mes connaissances sont sommes toutes assez limitées, même si mon bagage technique me permet de comprendre où je vais.
Je n'exclus donc pas d'explorer les pistes que tu me donnes, notamment celle de hiérarchiser les ISR. Sur l'Uno c'est je crois impossible, c'est pourquoi j'ai implémenté ce système de hierarchie d'appel des fonctions depuis la boucle principale. Ca fonctionne assez bien, même si ça a un coût en terme d'utilisation processeur: les boucles récursives et leurs tests associés prennent du temps. Sur la Due ça fonctionne aussi bien sûr, par contre après ta remarque j'ai jeté un œil à la doc: il est tellement simple de hierarchiser les ISR qu'en effet, je risque d'adopter cette solution, tant elle devrait amener une économie de ressources!

Simplement, le lien entre tout ça et la liaison série m'échappe. Comme je l'expliquais, comme pour chaque coordonnée du programme final je veux générer une "route", un itinéraire, avec une vitesse déterminée, je peux accélérer la vitesse d'exécution du programme tant que je veux, j'aurais toujours un cas où un mouvement (ou plutôt un ensemble de mouvements correspondant à un déplacement entre deux coordonnées) sera plus long que les buffers mis en place.
Il y aurait bien sûr la solution d'un buffer assez gros pour tous les cas de figures (comme une carte SD, par exemple), mais ça me semble aller à l'encontre du bon sens, sauf si l'on veut rendre le système autonome.

Du coup, je reste bien sûr ouvert aux améliorations proposées. Pour les ISR, je l'ai expliqué plus haut. Pour le format d'envoi des données, je n'en vois pas de plus compact que ce que ce que j'ai mis en place:
variable + valeur, chainées sans espace, avec un caractère \nl pour terminer la coordonnée.
D'abord copiées depuis le buffer de réception vers un buffer temporaire, vidées au passage des caractères inutiles, puis de la parsées une à une et stockées, puis interprétées, etc.
Mais j'ai le sentiment que tant que je n'aurais pas un contrôle de flux efficace, toutes les améliorations derrières seront inutiles, pour les raisons évoquées...

Encore merci.

Je ne comprends pas vraiment ce que tu veux faire, mais sur la question du Serial, avec une carte DUE, il n'y a pas de goulet d'étranglement si tu utilises le Native USB Port car c'est de l'USB on the GO high speed (soit au mieux 480 Mbps).

Une fois que ton PC a envoyé des données à la carte DUE via SerialUSB.Read, que tu as rempli ton buffer, tu peux le renvoyer vers un équipement externe à la carte via un DMA pour accélérer le transfert.

Coté mémoire, la RAM se compose de 96K + 4K non contigus, donc elle n'est pas très importante mais suffisante par exemple pour faire du streaming video en niveaux de gris.

Merci pour la réponse!

Ce que je veux faire: on pourrait comparer ça au croisement entre une imprimante et un laser de spectacle. C'est extrêmement proche de ce projet, mais le but et l'implémentation en sont très différents.
Dans le cas du projet du lien, des images abstraites sont générées à partir de sons, et les galvanomètres sont pilotés par (il me semble), la carte son du PC, via Max.
Dans mon cas je veux envoyer une image, pixel par pixel.

Je ne me suis pas encore penché sur la liaison USB native de l'Arduino Due. J'ai fais un essai avec les librairies officielles et l'IDE, et je ne suis pas parvenu à avoir une communication (sur le principe de "un caractère reçu, un caractère renvoyé).
ce qui me fait peur avec cette solution, c'est ce que ça va m'amener à développer coté PC? Avec la liaison série, les bibliothèques existent, avec des méthodes qui sont portables sur Linux, OSX Windows. Est-ce que ce sera le cas avec une liaison USB?

La mémoire est suffisante dans mon cas: puisque les données sont bufferisées à plusieurs niveaux, ce qui est surtout important c'est que les différents calculs puissent se faire entre deux mises à jour de la position du laser. C'était juste avec l'Uno, avec la Due c'est bon, même si je n'ai pas encre fait de mesures précises.

Pour le DMA, ça veut dire Direct memory acces, c'est ça? Alors, je vais peut-être dire une bêtise, mais je crois que je suis déjà un peu dans ce cas-là. Disons à moitié. J'utilise une liaison I2C pour envoyer mes coordonnées (sur 16 bits) aux pilotes des deux galvanomètres, qui fonctionnent en analogique. Or la liaison I2C, si je me souviens bien, est gérée comme ça: on envoie les données avec la méthode write, qui les stocke dans un buffer, puis active un drapeau, après quoi c'est la routine d'interruption qui s'occupe de tout. C'est bien ça, non?

Je vais essayer de comprendre ce que dit la doc atmel sur l'USB natif.

Par rapport à ce que j'ai maintenant compris de ton projet, il me semble que faire dialoguer via Serial la carte Arduino DUE avec le PC à travers Processing (un substitut de java avec de très nombreuses bibliothèques) serait un bon moyen de piloter finement les flux entre PC et DUE.

Pour info, la fenêtre de l'IDE arduino est développée sur le modèle de Processing, de même le Traceur Série que tu as dans Menu>Outils>Traceur Série) et tu peux trouver un tutoriel basic de Processing ici:

http://www.lakos.fs.uni-lj.si/images/Predmeti/MK/2014/C%20Programming%20for%20Arduino.pdf
plus particulièrement à partir de la page 155.

Edit: Et aussi ce lien, tutorial Sparkfun pour un flow control Processing <->Arduino :
https://learn.sparkfun.com/tutorials/connecting-arduino-to-processing

Sur la question du DMA, ça ne fonctionne pas sur le modèle du TWI (I2C pour Atmel), ça permet de faire des transferts de periphérique (au sens Atmel donc contrôleur par exemple ADC ou DAC, ou mémoire ) à periphérique (ou mémoire) très rapidement et (pratiquement à plus de 95%) sans utiliser de cycles d'horloge de la CPU.

Ceci a pour conséquence qu'utiliser un DMA te permet d'utiliser la CPU dans la loop pour faire tout autre chose en même temps (ce n'est pas du temps CPU partagé, ce sont bien des opérations qui se font en parallèle).

Bonjour,
quelques réflexions concernant le buffer :

il se remplit à l'écriture :
min ooooooooooooooooooooooooooooooooooooooooo>----------------------------------------------- max

se vide à la lecture :
min oooooooooooooooooooo<----------------------------------------------------------------------------- max

le xoff est renvoyé quand on s'approche dangereusement du max. De la profondeur supplémentaire est ménagée (rab) pour pallier le manque de réactivité de l'émetteur. La taille du rab est déterminée expérimentalement, elle dépend du matériel, de l'OS, de l'appli, etc :
min ooooooooooooooooooooooooooooooooooooooooooooooooooo> xoff-----------rab------------- max

le xon se génère quand on s'éloigne confortablement du max. Cet hystérésis est pour éviter la multiplication des échanges xoff xon:
min oooooooooooooooooooooooooooooooooo< xon---------------------------------------------------- max

en réalité le buffer est circulaire
se remplit à l'écriture :
min --------------------------tail oooooooooooooooooooooooooooooooo head >----------------------max

se vide à la lecture :
min ------------------------------------------------> tail oooooooooooooo head -----------------------max

l'écriture quand head va au-delà de max :
min oooooooooooo head>------------------------------- tail oooooooooooooooooooooooooooooooomax
à se stade il faud vérifier que head ne dépasse pas tail, et générer une erreur si cela se produit

pour savoir quand il faut générer le xon, il faut, à l'écriture, vérifier si
if ( (head - tail) > (max - rab) ) // on envoie le xoff une seule fois
je pense que le fait de dépasser le max et de revenir au min n'a pas d'influence dans ces calculs, mais je laisse le soin de le vérifier

pour mémoire la lecture quand tail va au-delà de max :
min --------------------------tail oooooooooooooooooooooooooooooooo head >----------------------max
du déjà vu

je vous fais grâce de la formule concernant le xon, ceux qui ont suivi jusqu'ici sauront comment le générer

à troisiemetype : as-tu implémenté tout ceci dans ton soft ?

reprenons le xoff :

le pc envoie les données :
pc --------- [bytes] ---------> aduino

si l'arduino ne peux plus suivre, il renvoie xoff
pc <-------- [xoff] ---------- aduino

que se passe-t-il dans le pc ?
il y a aussi un buffer propre au port com :
appli ----[bytes] ----> buffer du com --------- [bytes] ---------> aduino

quand l'arduino envoie le xoff, le buffer du com se remplit :
appli ----[bytes] ----> buffer du com <--------- [xoff] --------- aduino
quand le buffer du com déborde, on a aux mieux une erreur, au pire des pertes de données

il faut donc absolument que l'appli sache quand l'arduino ne suit plus et qu'elle cesse d'envoyer des données. Elle peut faire autre chose ou s'arrêter

comment l'appli peut savoir que le buffer du com risque de déborder ?

  • en observant régulièrement sa longueur
  • ou en se tenant au courant des xoff xon envoyés par l'arduino (génération d'une interruption dans le pc

voilà pour ce qui est du plus simple, car comme les OS sont multitâche voire multithread, il y a encore plein de buffers partout à l'intérieur du pc, qui peuvent amener quelque surprise

on voit donc que de toutes façons, l'appli doit savoir ou en est l'arduino, et se mettre à son rythme
AMHA il n'est pas utile d'utiliser toute cette implémentation xon xoff : quand l'arduino est débordé, il suffit d'envoyer un message "stop" à l'appli, qui se calmera jusqu'à ce que l'arduino lui renvoie un "go"
is that simple !
je dirais que xon xoff a une certaine obsolescence

bon c'est encore moi
je ne recommande pas la lecture de ce qui suit ...

soit un message à envoyer du pc ver l'arduino :

"X0123Y4567"
cela doit rappeler quelque chose à certains, disons qu'on envoie une coordonnée selon un axe X, dont la valeur est comprise entre 0 et 9999 et pareil pour un axe Y

rien de plus simple à première vue, voyons comment l'arduino traite ceci :
d'abord il reçoit ce string sous forme d'une suite de caractères, qu'il trouve dans son buffer :
"X" "0" "1" "2" "3" "Y" "4" "5" "6" "7"

caractères ? connait pas ! voici ce qu'il voit en réalité
88 48 49 50 51 89 52 53 54 55

ce qu'il doit en faire : à la réception du 88, il sait que la suite constitue la valeur pour l'axe X, écrite sur 4 chiffres. J'ai volontairement mis un 0 non significatif au début pour garder une longueur de string constante, c'est plus simple pour l'arduino (si non il doit continuer jusqu'au 89 pour savoir le nombre de chiffres)

puis il prend le premier chiffre : 48, auquel il enlève 48 pour avoir sa valeur : 48 -48 = 0
puis il vérifie que 0 est bien un chiffre compris entre 0 et 9, si non, erreur
puis il multiplie le résulat par 1000
0*1000 = 0
garde ce résultat en mémoire

pareil pour le suivant : 49 - 48 = 1
1*100 = 100
100 en mémoire

puis 50 - 48 = 2
2*10 = 20
20 en mémoire

puis 51 - 48 = 3
3 en mémoire

enfin il additionne le tout
0 + 100 + 20 + 3 = 123

pareil pour le Y
simple en effet ...

je proposerais une autre méthode (en exclusivité mondiale aujourd'hui)
on n'envoie plus de lettre mais des chiffres
le pc envoie :
128 0 123 35 87

pour l'humain, c'est incompréhensible, mais pour l'arduino ?

d'abord il réceptionne le 128 : c'est une commande. Dans ce protocole, les valeurs entre 0 et 127 sont des nombres, les valeurs entre 128 et 255 sont des commandes. Disons que le 128 indique le début d'un message pour les axes X et Y

l'arduino prend alors le premier nombre 0, qu'il multiplie par 128
0*128 = 0, en mémoire (ou dans un registre)

puis
0 + 123 = 123
on a déjà la valeur de l'axe X : par superstition je ne dirais pas "plus simple, tu meurs !"

il sait que la suite correspond à l'axe Y
35 * 128 = 4480

puis 4480 + 87 = 4567
cqfd !

l'arduino étant une chose taillable et corvéable à merci, je trouve qu'on lui a un peu trop simplifié la vie !
on va lui envoyer
128 0 123 35 87 v

v étant un nombre de vérification, calculé en fonction des nombres qui le précède
comme il s'agit d'un nombre et pas d'une commande, il sera obligatoirement compris entre 0 et 127
cela amène le minimum de robustesse indispensable dans le domaine du make (entre autres !)

Merci à vous pour vos réponses!

ard_newbie, je crois que je j'ai de la lecture pour les deux jours à venir. Le temps de vous répondre et je commence la lecture du document dont tu m'as donné le lien.
Pour la question du DMA, effectivement sur le papier c'est tentant. Dans la pratique, je ne vois pas l'implication directe que cela peut avoir: les positions sont mises à jour via un DAC 16bits commandé par I2C, du coup il faudrait changer de système pour implémenter un DMA, non? Il faudra que je jette un œil à la doc Atmel, là aussi.

Trimarco232, merci pour toutes ces précisions. Pour les buffers, c'est à peu près ce que j'ai implémenté. Ce sont des buffers circulaires (comme tu le faisais remarquer dans ta première réponse) avec un "pointeur" (entre guillemets car c'est plus un index qu'un vrai pointeur au sens littéral) vers la tête, et un autre vers la queue. L'ISR associée à la réception copie le caractère du buffer du processeur dans mon buffer circulaire, à l'index "head". Et lorsque je veux accéder à la valeur du buffer je le fais à l'index "tail". Les index sont incrémentés à chaque écriture/lecture (respectivement), sauf dans le cas où l'on veut simplement connaître la valeur sans la lire (peek()).

A ceci s'ajoute effectivement la vérification de la taille du buffer. Vérification qu'il y a des données dedans lors de la lecture (tail != head), vérification qu'il y a de la place lors de l'écriture ((head - tail)%size < size). La place disponible est également vérifiée à chaque écriture, et comparée au seuil de Xoff (dans le cas ou xon a déjà été envoyé, ou que le seuil n'a pas été atteint. Ce seuil a été arbitrairement fixé à 96 pour une buffer de 128. Lors de la lecture le seuil bas est également vérifié (uniquement si Xoff a été envoyé précédemment), afin de redemander des données lorsque le buffer se vide. Ce seuil là a été arbitrairement fixé à 32.

L'ensemble du code généré est très proche de ce que l'on trouve dans le projet GRBL, même s'il a été débarrassé de ce qui n'était pas utile à mon projet.

Les essais que j'ai fait avec coolterm montrent donc, c'est ce que je disais l'autre jour, que quand je désactive le flow control (coté coolterm, donc), l'arduino envoie bien les caractères Xon et Xoff, une fois de chaque. Ils sont biens reçus, puisque je les vois dans l'affichage hexadécimal.Une fois que j'active xon/xoff dans Cooterm, les caractères n’apparaissent plus, je pense que coolterm les masque puisqu'ils "deviennent" des caractères de contrôle. Mais pas d'impact.

J'ai tenté de modifier les seuils pour Xon et Xoff (xoff envoyé à 64, puis 32, même 16 caractères), d'augmenter la taille du buffer coté arduino, d'abord 256, puis 512, puis 1024 caractères, je reste avec quelque chose qui fonctionne visiblement puisque les caractères xon et xoff arrivent, mais qui pratiquement ne fonctionne pas, puisque la réaction ecomptée n'arrive pas.

Il est en effet très possible que coolterm soit en cause, et en particulier les buffers liés à l'implémentation des solutions logicielles...

pour coolterm, je ne peux pas t'aider
je viens d'un temps où il fallait tout faire soi-même, les outils étant trop chers ou inexistants
la facilité qui consiste à essayer une autre bibliothèque ou un autre logiciel quand cela ne fonctionne pas, je ne peux pas l'utiliser
en fait, je maitrise tout mais je n'avance pas ...
alors il faut que je laisse les autres vivre avec leur temps
utiliser l'existant, en relation avec un groupe, c'est une vertu
tu comprends vite, tu y parviendras !

Pour continuer sur l'idée de buffer, ma première idée était effectivement de ne pas m'embêter, et d'adopter le flux suivant:
Envoi d'une chaîne de coordonnées
Arrêt de la communication
Parsage/enregistrement des valeurs
Envoi d'une demande pour la coordonnée suivante.

Cela fonctionnait très bien, mais il m'a semble que l'implémentation d'un Xon Xoff permettrait de simplifier l'ensemble du système (c'est à dire, Arduino plus programme coté PC), en n'imposant pas par exemple l'utilisation du programme que je développe, mais en pouvant s'appuyer sur un simple terminal. Ou un autre système prenant en charge le contrôle de flux, sans qu'il y ait besoin de développer autre chose qu'un formatage éventuel des coordonnées.

Bref, l'idée est de rendre le projecteur plus souple, plus universel. Pour l'instant c'est peut-être un peu stupide, mais je n'exclue pas un débouché commercial de ce projet, donc j'essaie d'avoir à l'esprit des notions de compatibilité ultérieures, pour ne pas avoir à tout réécrire le jour où le produit est définitivement au point. L'inconvénient est, c'est vrai, que je passe beaucoup de temps sur des questions qui pourraient être aisément contournées!

Merci pour les éclaircissements sur ce que peut être un protocole de communication plu efficace. En effet, dans mon cas c'est assez proche de Gcode, avec l'avantage de pouvoir envoyer des coordonnées à la main. Dans mon programme le parsage se fait effectivement à peu près comme ça, à partir d'une lettre on cherche un nombre, et le parseur a un état, en fonction de cet état et des données qu'il lit sait qu'il traite une nouvelles valeur, un nombre, ou bien qu'il a une paire complète et l'enregistre. Et enregistre l'ensemble des paires lorsqu'il trouve une fin de ligne. En effet, et bien que j'ai fait mon possible pour rendre l'ensemble assez fluide (là aussi ça ressemble d'assez près à GRBL et TinyG, des causes similaires impliquant des solutions similaires), ça reste assez lourd, et répétitif.

A première vue j'ai eu l'impression que la solution que tu décris n'était peut-être pas si légère que ça. Puis je me suis quand même rendu compte que d'une part le traitement est beaucoup, beaucoup plus rapide. Entre une série de tests en cascade et une paire d'additions et de multiplications, je crois que le choix est vite fait. Et puis, j'avais l'impression que ce formatage imposait l'envoi de toutes les coordonnées. Bien sûr ce n'est pas le cas, mais il m'a fallu une deuxième lecture pour m'en rendre compte: la chaîne de contrôle permet évidemment de gérer finement les combinaisons, et de définir si les données reçues concernent une coordonnée en X, un en Y, les deux, une vitesse, etc.

Je vais réfléchir à ça. Mais ça risque de prendre un peu de temps si je veux aussi comprendre comment fonctionne le port USB natif, si ça vaut le coup de l'utiliser, voir si processing est plus approprié pourla génération de mes données, et tous les points abordés ici...

Merci encore!