PCM pour MBED

Avec ma trame 520 modifiée, j'ai une partie de la réponse.
Après le 520, les 4 octets suivant représentent le volume consommé, les 8 octets suivants la somme (faite coté MBED) des volumes consommés, les 6 octets suivants le time stamp en microseconde au moment de la lecture du CAN par le MBED.

On peut voir que la somme est juste, trame après trame, donc ce n'est pas le Pc qui duplique les trames, mais elles sont comme dupliquées dans le buffer, pour qu'au moment où on lit le buffer pour envoyer au Pc elles sont en double (par exemple la stampée 11b066).

On peut aussi voir que quand il y en a une en double, parfois elle remplace une trame manquante en se basant sur le temps entre deux trames, qui est de l'ordre de 0,8 à 1 seconde, temps entre 20 tours de moteur, à environ 1200 tours minute. Ainsi la 11b066 est en double, mais elle est en trop et ne remplace aucune manquante, car il y a le "bon" temps entre 050711, 11b066 et 1ebfe0. Par contre, la 2c24fe est en double et semble remplacer une trame manquante. Et sans qu'il y ait doublon, il manque une trame avant la 71b2a6.

Bref des trames en plus, d'autres en moins, d'autres remplacées, pas étonnant que mon volume consommé soit faux par rapport à l'odb. Mais le plus remarquable, c'est que ces problèmes semblent peu affecter le calcul du volume par la méthode PCM (intégration en utilisant le pid 520 et le pid3C8 lui aussi chahuté).

520 04a2 000004a2 e050e6
520 036a 0000080c ec7964
520 03b6 00000bc2 f8b678
520 0381 00000f43 050711
520 0308 0000124b 11b066
520 0308 00001553 11b066
520 0320 00001873 1ebfe0
520 035d 00001bd0 2c24fe
520 035d 00001f2d 2c24fe
520 03c3 000022f0 479d07
520 03bf 000026af 559d99
520 03d4 00002a83 71b2a6
520 03f1 00002e74 7fb74a
520 01ef 00003063 8f38ad
520 01ef 00003252 8f38ad
520 0189 000033db 9fcd2e
520 0195 00003570 afe83c
520 0196 00003706 bf59ca
520 018f 00003895 ce5e5e
520 01be 00003a53 dccfa2
520 025a 00003cad ea6b7f
520 02aa 00003f57 f7fc09
520 02cb 00004222 05b88e
520 02e2 00004504 138525
520 02e8 000047ec 2179a6
520 02f6 00004ae2 2f8a36
520 02c7 00004da9 3daac7
520 0139 00004ee2 4de600
520 0139 0000501b 4de600
520 0000 0000501b 6f9fc1

Autre fait remarquable, ce cahot semble s'arrêter au bout de quelques minutes (2-3 ) pour un trajet d'une vingtaine de minute. C'est d'ailleurs ce que j'avais constaté concernant la conso odb qui ne continuait pas à diverger comme elle le faisait au début du trajet, mais semblait devenir conforme.

Je vais regarder si le cahot s'arrête aussi coté pid3C8
 
communique moi ton projet mbed, je jetterai un oeil neuf sur la gestion du ringbuffer.
pour info, gary utilise un RB de 2048 entrées, mais en stockant de manière particulière la partie data[] dans une "memorybank" secondaire, cf son appli...
 
Effectivement, j'observe bien la même chose.
J'affiche la différence entre les time stamps de 2 trames qui se suivent, pour chaque trame.
cahot.PNG

(j'ai tronqué à 300000 le graphe en y)
On peut voir que jusqu'au point 4800 environ (x 64 ms = 307 s = 5 min), c'est complètement cahotique. Des trames inversées, des trames dupliquées, des trames manquantes, à la pelle.
Et après cette période, jusque la fin du trajet (environ 33 min), par ci par là, une trame manquante (c'est le petit pic).
 
Je suis inquiet avec ce bout de code
Code:
void can_check(void) {
    led3 = 1 ;
    tct = timer.read_us();
    
    while ( can2.read(can_MsgRx) ) {  <=====  cette ligne

        rb_write_pointer++ ;                //  increase write_pointer
        rb_write_pointer &= BUFFER_MASK;    //  loop if at end of array
en en particulier la ligne que j'ai marqué
en effet je boucle tant que je peux lire

mais ailleurs, j'ai
Code:
can2.attach(&can_check);
qui lance cette boucle quand il y a aussi quelque chose à lire

n'a-t-on pas le risque qu'il fasse les deux en parallèle ? et mette en buffer 2 fois la même ligne.
 
J'avais évoqué ici des trames inversées, il me semble que j'ai aussi des trames en double et des trames manquantes. Ceci pourrait expliquer la bizarrerie concernant la consommation évoqué dans une autre thread (celle sur l'arrondi de la conso). Est-ce comme l'évoque Priusfan un pb de buffer en lecture coté Windows PCM ou est-ce un bug ou une limitation coté MBED CAN ? Ou encore un bug que j'aurais créé dans le ring buffer coté mbed en voulant contrôler le buffer overflow ? à suivre

Je n'ai jamais vu d'inversion sur le pid 230. Tu as un timestamp associé ?
Un bug dans un buffer me semble une bonne hypothèse.
As-tu essayé de mettre une seconde capture en paralllèle ?
 
C'est en cours.
 
Je suis inquiet avec ce bout de code [..]

Pour se prémunir du risque évoqué, il suffit d'utiliser en variable globale un booléen, e.g. :
Code:
volatile bool inside_can_check = false;
qui est testé en début de fonction, si vrai on sort de suite, si faux on le met à vrai puis on exécute la fonction et on le met à false en sortie du while; ça coûte pas cher à tester en tout cas.
 
can2.attach(&can_check) est utilisé une fois seulement à la fin de la routine init() et permet d'associer une routine à un "interrupt"; il n' y a donc aucun risque de ce coté là.

par contre, après analyse de ton appli, je constate que tu fais énormément de manipulations de strings pour générer des logs.

en effet, tu relaies toutes les trames reçues vers la log et je pense que cela pourrait bien saturer de ce coté...

combien de trames/sec balances tu vers la log??
envoies ton appli @guinness, il a l'oeil du pro pour cela (tout se passe dans main.cpp)
 
C'est justement l'association avec l'interrupt qui l'inquiète: si l'interrupt se produit alors que l'on est déjà dans la fonction (par exemple en train de tester la fin du while et on voit que can2.read nous donne qqchose), alors l'interrupt lance a nouveau la fonction (mais dans ce cas can2.read devra retourner la suite de ce qui a été lu dans le premier appel) et on peut avoir une lecture hors séquence car ce n'est qu'en sortant de l'interrupt que le premier appel de fonction continuera à être exécuté.
Pas forcément très probable mais il faut toujours se méfier des choses non probables quand on programme, tester avec le booleen devrait éliminer cette hypothèse facilement...
 
Et si pendant que je suis dans la boucle une nouvelle trame arrive et fait un interrupt ?

Mais comment le programme gère-t-il à la fois d'appeler can_check par interrupt, quand des données proviennent du bus Can, et d'un autre coté boucler tant qu'il y a des le données sur le bus Can ?

Intéressante ton idée de mettre un booléen pour simuler un singleton pour être sur de ne pas rentrer dans la boucle sans en être sorti.
 
Mais comment le programme gère-t-il à la fois d'appeler can_check par interrupt, quand des données proviennent du bus Can, et d'un autre coté boucler tant qu'il y a des le données sur le bus Can ?

ça dépend de l'OS et/ou des options de compilations: soit on est en multi-thread soit on est en mode interrupt "pur" comme au temps de MS-DOS.
Dans le premier cas les deux vont s'exécuter en parallèle (éventuellement simulé si un seul processeur, c'est-à-dire que n instructions vont s'exécuter pour l'une des taches, puis k instructions pour l'autres, etc.); dans le deuxième cas la routine en interrupt interrompt la première qui ne reprendra que lorsque l'interrupt aura fini sa boucle (et si elle avit déjà fait le read alors elle travaillera sur des données en retard par rapport à tout ce qu'aura traité la partie interrupt.
Reste la question de deux appels simultanés à can2.read, est-ce que cette fonction est ré-entrante?
 
J'ai commencé des essais avec la boucle protégée par la variable volatile, et sans boucle mais aussi sans protection. Je pense qu'il faudra peut-être aussi essayer sans boucle et avec protection.

Voici du code pour comprendre :
(pour info je n'ai pas la led4 qui clignote, ni même s'allume, donc pas de buffer overflow)

Code:
volatile bool inside_can_check = false ;
volatile unsigned int rb_write_pointer = 0; // ring buffer write pointer
volatile unsigned int rb_read_pointer = 0;  // ring buffer read pointer
volatile unsigned int rb_data_size = 0;     // ring buffer number of entries

void can_check(void) { // called by interrupt

    if (!inside_can_check) {
      inside_can_check = true ;
      while ( can2.read(can_MsgRx) ) {

        rb_write_pointer++ ;                //  increase write_pointer
        rb_write_pointer &= BUFFER_MASK;    //  loop if at end of array
        rb_data_size++ ;                    //  ring buffer number of entries

        if ( rb_write_pointer == rb_read_pointer && rb_data_size == BUFFER_SIZE) {  // buffer overflow
            fprintf(stderr, "buffer overflow\r\n");
            led4=!led4;
            bufferoverflow=true;
            rb_data_size = 1;
            rb_write_pointer = 1;
            rb_read_pointer = 0;
        }       // error

 
        // add entries in the ring buffer
        second[rb_write_pointer] = timer.read_us();
        can_id[rb_write_pointer] = can_MsgRx.id & 0xFFF;
        msg_length[rb_write_pointer] = can_MsgRx.len;
        for (char i=0; i<can_MsgRx.len; i++) {
            data[rb_write_pointer][i] = can_MsgRx.data[i];
        } // for

      } // while
      inside_can_check = false ;
    }
} // can_check

int main() {
    init();  //  executes:     can2.attach(&can_check);
       
    while (1) {
        // read CAN messages from the buffer       
        if ( rb_data_size > 0 ) {               // new message is available
            rb_read_pointer++;                      // position to next
            rb_read_pointer &= BUFFER_MASK;         // loop if at end of array
            rb_data_size--;                         // update nbr of entries in the ring buffer

            // we store in w_vars the results : it is not funny to manage all these [][]
            w_id=can_id[rb_read_pointer];
            w_len=msg_length[rb_read_pointer];
            w_sec=second[rb_read_pointer];
            w_num=rb_read_pointer;
            for (int i=0; i<w_len; i++) {
                w_data[i]=data[rb_read_pointer][i];
            } // for

            wrkmsg();               //  decide what to do with the message  (w_vars)
            
        } //        end // if ( rb_data_size > 0 )
    } // while
} // main

Normalement je devrais avec ça protéger la lecture mais aussi le stockage des données dans le buffer.
Normalement si je rejette malencontreusement un accès à can_check la fois d'après il devrait lire ce qui a été raté, car il n'a pas fait de can2.read.
 
cela me parait bien verrouillé :jap:

de mon coté, pour une simple question de consommation, je procèderai à des tests en rajoutant le délai initial dans le cas du "sleeping", càd quand le nb de trames est faible, car il parait qu'une boucle sans délai fait consommer + que lorsqu'il y a un délai... je vais donc insérer un ampèremètre dans l'alim pour chiffrer cela.
 
Bien, la conclusion c'est que ce n'est pas à la lecture du bus Can, ni au stockage (au moment du stockage) dans le ring buffer, que le cahot se produit. Par contre je perds une trame sur 300 environ en me basant sur l'écart prévisible entre 2 trames. Il faudra que je regarde plus tard à quoi c'est du. Mais ce n'est pas le plus grave. C'est le cahot le plus grave.

Je vais donc mettre plus de traces coté lecture du ring buffer. Mais le plus probable maintenant est coté communication bluetooth.
 
de mon coté, pour une simple question de consommation, je procèderai à des tests en rajoutant le délai initial dans le cas du "sleeping"

T'inquiète. J'ai laissé le sleeping. Mais pour la démo c'était plus simple.

je vais donc insérer un ampèremètre dans l'alim pour chiffrer cela.

Ça m'intéresse. Regarde en le faisant dormir moins longtemps : 0,5 0,3 0,2 0,1 0,05 0,03 0,02 0,01 secondes
 
En analysant mes logs, j'ai vu que l'idée de Guinness a été utile (en tous les cas elle ne peut pas être néfaste). 1 % des trames environ étaient traités dans une concurrence entre la boucle while et l'appel sur interruption. Et c'était l'appel sur interruption qui gagnait. Encore que le fait de mettre des traces dans la boucle rallonge la durée de la boucle et peut-être suffisamment pour que la condition de la boucle soit remplie.

Aujourd'hui, contrôle au niveau de la lecture du ring buffer. Les motifs sont dans le bin. Mais je sens que c'est dans la comme bluetooth avec PCM qui ne marche pas bien. Mais comment dans ce cas les trames seraient doublaient ou inversaient. A suivre.

Priusfan n'a surement pas mes problèmes car il a beaucoup moins de trames passives, et aussi car il transmet assez peu en bluetooth.
 
....Mais je sens que c'est dans la comme bluetooth avec PCM qui ne marche pas bien. Mais comment dans ce cas les trames seraient doublées ou inversées ?. A suivre.
saturation serialbuffer ???? (mauvaise gestion ringbuffer du serial?)
 
Je ne comprends toujours pas, mais je me rapproche du nœud.
J'enregistre coté MBED (au moment de la lecture du ring buffer, mais il se passe ensuite dans choses avant que cela soit envoyé PCM), puis lorsque c'est reçu par PCM.
J'ai du chaos (désolé pour la faute depuis quelques postes, pour ceux qui veulent apprendre :
http://grammaire.reverso.net/2_1_12_cahot_chaos.shtml )
des deux cotés, même s'ils n'ont pas la même forme. Coté PCM, c'est décrit plus haut : frame manquante, frame inversée, frame doublée, mais frame propre. Coté MBED, pendant la phase chaotique, la forme est différente, avec plus de trames que coté PCM (ou plutôt ce serait PCM qui aurait moins de trames), mais malformées. Puis quand cela s'arrange d'un coté, cela s'arrange de l'autre et c'est en phase comme coté lecture du Can.
cahotreadbuffer.PNG


voici à quoi ressemble les trames

3C800282A000023A3DCA37E39
3C800282A000023A3DDA38B70
3C800282A000023A3DEA395A0
3C800282A000023A3E4A3D7CA
3C8B3E302000000D1A3E4C86AD1
3C800282A0000003FA3E5CECF03
3C800282A000119562CA3E8AE3195
3C800282A000023A3E4A3D7CA
3C810000000091E2BB0A3EACFBBFB
3C800282B000024A3EBA5B923
3C800A300DFD1A3EBD4AC2B
3C800001EB2A3ECC0855A
3C800282A000023A3EDA43985
3C8007A020000006DA3EECBFBC3
3C800282A0000A3EFBC7BF0
3C800282A000023A3F0A78E1D
3C800000000092A2BBCA3F0CF6C23
3C800282A0001198D63A3F1C42552
3C800282A000023A3EDA43985
3C800282A000023A3F3A4C9B2

à suivre. Mais du coup, cela pourrait ne pas venir de bluetooth.
 
je n'ai pas d'idées sur l'origine réelle du pb...

suggestion: alléger au max l'application en recréant

  • a) une appli minimale de collecte (avec les filtres) qui seulement fait un "forward" en USB.
  • b) on la complète jusqu'au moment où cela coince..


coté consommation électrique 12V de l'interface mbed:
sur la iOn, elle oscille aux alentours de 60 mA.
le fait de passer en mode sommeil est à peine visible.

quand j'aurai le courage, je tenterai de couper le régulateur de tension dans le cas de faible trafic.
c'est quand meme assez lourd car je dois utiliser un oscilloscope, un potar de xxx MOhms et qqs capas de différentes valeurs et de bonne qualité pour ajuster la constante de temps (et le résultat est loin d'etre garanti 8) ).
 
en fait j'avais un doute sur ce qui se passait entre le stockage et la lecture du ring buffer.
c'est pas le chaos (maintenant que je sais l'écrire), mais le b????l (que je ne peux écrire).
et dès la première microseconde

juste pour vous donner un idée, voici les premières microsecondes :
<<038070478B64F00800008000000C7
debug:1,0,0
>153F051D61003701D601375480
>253F051D61003701D601375480
<<039041D62536A012E00006B
debug:2,1,0
>1038070478D65302800008000000C7
>2038070478D65302800008000000C7
<<039041D627367022E000F7A
debug:3,2,0
>103B050478DD6D03000000E020
<<3C8061D6277A203003400000005
>23C8061D6277A203003400000005
debug:4,3,0
>1038070478F65804800008000000C7
>2038070478F65804800008000000C7
<<039041D629368042E000F7A

pour vous aider à comprendre
<< c'est écrit au moment de la mise dans le ring buffer
>1 c'est écrit au début de la boucle de lecture du ring buffer
>2 c'est écrit à la fin de la boucle de lecture du ring buffer

ensuite :
les 3 octets qui suivent : le pid
les 2 octets qui suivent : la longueur des données
les 8 octets qui suivent : le timestamp de la trame au moment du stockage dans le ring buffer
les 2 octets qui suivent : la position dans le ring buffer
les octets qui suivent : les données

les lignes debug sont dans la boucle de lecture et sont au tout début de la boucle; elles indiquent dans l'ordre : la position à lire dans le ring buffer, la position à écrire dans le ring buffer, et la taille du ring buffer sans la donnée en cours de lecture

donc la première trame est une 038, elle est stockée en 00, à l'heure 0478B64F, mais elle ne ressort jamais
la première qui ressort est une 53F, stockée en 01, à l'heure 1D610037

il ne lit pas la 00, mais la 01, qui a probablement été stockée lors du dernier démarrage de la Prius.

bref déjà un problème.

deuxième problème :
la deuxième trame stockée est une 039, dans la position 01, à l'heure 1D62536A, ne ressortira pas
mais celle qui ressort est une 038, en position 02, à l'heure 0478D653

c'est à ce demander si ce qui est imprimé est juste.

mais encore pire, la troisième trame qui ressort pendant que la quatrième arrive
en début de boucle >1 c'est une trame 03B, mais comme avant de finir la boucle il arrive << une trame 3C8, c'est elle qui ressort en fin de boucle >2

j'avais dit que c'était le b????l

il suffit de revoir le code, imaginer que la lecture et l'écriture se font en parallèle, concurrence, pour comprendre que cela ne va pas.

juste un exemple, en début de boucle d'écriture dans le buffer, j'incrémente la taille du buffer. Comme la taille est non nulle, la boucle de lecture du buffer peut lire, mais je n'ai peut-être rien écrit encore.

bref le code n'est pas écrit en pensant à la concurrence.

et bien je vais essayer de rendre plus robuste cela même sans guiness et sans guiness.

à demain
 
Dernière édition:
En mettant les incréments et les décréments à la fin des boucles, cela devrait marcher.
See you tomorrow. Too late to try now in the car.

Code:
volatile bool inside_can_check = false ;
volatile unsigned int rb_write_pointer = 0; // ring buffer write pointer
volatile unsigned int rb_read_pointer = 0;  // ring buffer read pointer
volatile unsigned int rb_data_size = 0;     // ring buffer number of entries

void can_check(void) { // called by interrupt

    if (!inside_can_check) {
      inside_can_check = true ;
      while ( can2.read(can_MsgRx) ) {

        // add entries in the ring buffer
        second[rb_write_pointer] = timer.read_us();
        can_id[rb_write_pointer] = can_MsgRx.id & 0xFFF;
        msg_length[rb_write_pointer] = can_MsgRx.len;
        for (char i=0; i<can_MsgRx.len; i++) {
            data[rb_write_pointer][i] = can_MsgRx.data[i];
        } // for

        rb_write_pointer++ ;                //  increase write_pointer
        rb_write_pointer &= BUFFER_MASK;    //  loop if at end of array
        rb_data_size++ ;                    //  ring buffer number of entries

        if ( rb_write_pointer == rb_read_pointer && rb_data_size == BUFFER_SIZE) {  // buffer overflow
            fprintf(stderr, "buffer overflow\r\n");
            led4=!led4;
            bufferoverflow=true;
            rb_data_size = 0;
            rb_write_pointer = 0;
            rb_read_pointer = 0;
        }       // error

      } // while
      inside_can_check = false ;
    }
} // can_check

int main() {
    init();  //  executes:     can2.attach(&can_check);
       
    while (1) {
        // read CAN messages from the buffer       
        if ( rb_data_size > 0 ) {               // new message is available

            // we store in w_vars the results : it is not funny to manage all these [][]
            w_id=can_id[rb_read_pointer];
            w_len=msg_length[rb_read_pointer];
            w_sec=second[rb_read_pointer];
            w_num=rb_read_pointer;
            for (int i=0; i<w_len; i++) {
                w_data[i]=data[rb_read_pointer][i];
            } // for

            wrkmsg();               //  decide what to do with the message  (w_vars)

            rb_read_pointer++;                      // position to next
            rb_read_pointer &= BUFFER_MASK;         // loop if at end of array
            rb_data_size--;                         // update nbr of entries in the ring buffer
            
        } //        end // if ( rb_data_size > 0 )
    } // while
} // main
 
je te suggère de reprendre le pgm initial de yoshi en y adaptant tes filtres;
il est ici : http://priusfan.info/canmonitor/mbed/CAN_MONITOR_YM.zip

(c'est de cela que j'avais démarré)

tu y ajoutes tous tes points de debug et on fait le bilan...

ce qu'il y a d'amusant, c'est que dans ce truc initial, il n'y avait aucun besoin de buffers supplémentaires
 
Merci, j'y jetterai un œil si je craque.
La il me semble que je tiens le bon bout.
Depuis que tu m'as fabriqué mon boitier, je n'ai pas arrêté de progresser.
Alors tout va bien, et j'ai passé d'excellentes vacances dans ma voiture.
C'est juste Tripmaster qui a été ralenti.
 
@thierry

:ovation:
cette histoire de gestion des pointeurs,c'est trop cong (comme ils disent dans le 31).
j'avais hésité à créer un modèle (que ce soit en excel ou en c++) pour vérifier la cinématique, mais je m'étais dit que les recettes de la toile étaient bonnes :oops: .
 
T'inquiètes. Je ne te serai jamais assez reconnaissant.
 
Pages vues depuis le 20 Oct 2005: 308,324,188
Retour
Haut Bas