Protocole de streaming de paquets pour microcontrôleurs PSP1N

Énoncé du problème


Lors du développement d'un autre appareil sur un microcontrôleur, je suis tombé sur une situation où l'enregistrement continu d'une grande quantité de données était nécessaire. L'appareil a dû enregistrer un ensemble de données composé d'un horodatage et de six mesures ADC de 100 fois par seconde sur une carte SD (appelons cet ensemble de données un paquet), puis analyser ces données sur un ordinateur sous forme de graphiques. Il était également nécessaire parallèlement à l'écriture de données sur une carte SD, en les transférant via UART. Ces données auraient dû prendre le moins de place possible, car il y a beaucoup de données. Dans le même temps, il était nécessaire de séparer ces paquets d'une manière ou d'une autre, car les données étaient transmises en flux continu. N'ayant rien fouillé sur Internet, je n'ai rien trouvé, il a donc été décidé de créer mon propre protocole et ses propres bibliothèques.


Et puis il est apparu - Protocole de streaming de paquets (PSP1N)


À la suite de certaines considérations, ce qui suit a été décidé: dans le protocole, les données seront transmises en paquets composés de N octets, où le premier bit de chaque octet est alloué au signe de bit de départ pour la synchronisation des paquets (d'où le nom du protocole), les sept bits restants sont alloués aux données. La séquence et la taille des données sont connues à l'avance.


Un exemple:


Nous allouons 32 bits pour l'horodatage, 60 bits pour les mesures ADC (6 canaux de 10 bits), pour un total de 92 bits. Comme vous pouvez transférer 7 bits de données utiles dans un octet, le paquet sera composé de 14 octets (92 bits / 7 bits = 13,14 ... arrondir à 14). Il y a 112 bits d'information sur 14 octets, dont 14 bits sont occupés par l'attribut de bit de départ de 92 bits de données utiles, il y a 6 bits inutilisés (dans lesquels nous pouvons écrire quelques informations supplémentaires, mais pour des raisons de simplicité, nous ne les utiliserons pas).



Lorsque le 7e bit est un signe du bit de début (indique le début du paquet), 6,5,4,3,2,1,0 sont des bits de données.


Le côté récepteur sait également qu'il reçoit un paquet de 14 octets dans lequel le premier bit du premier octet est le bit de début (1) (dans les octets restants, les bits de début sont 0), puis dans les bits de données dans l'ordre sont 32 bits de l'horodatage, puis 10 bits du numéro de mesure ADC. 1, puis 10 bits d'ADC # 2 et ainsi de suite ...


De même, l'écriture sur la carte SD et la lecture selon le protocole ont lieu. Au total, pour une journée d'enregistrement sur une carte SD, nous obtenons 115,4 Mo d'informations (14 octets x 100 mesures par seconde x 3600 sec x 24 heures).


Une telle structure de données est toujours pratique car à l'avenir, nous pouvons sélectionner des blocs de données de n'importe où dans le fichier et les afficher sous forme de graphiques, ne chargeant ainsi pas le fichier entier dans la RAM (qui peut atteindre plusieurs dizaines de gigaoctets). Et nous pouvons également implémenter un défilement pratique de ces graphiques en chargeant les packages nécessaires.



Il est temps de commencer l'implémentation logicielle du microcontrôleur


Nous écrivons la bibliothèque du microcontrôleur en C ++.


Pour plus de commodité, créez une classe:


class PSP { public: /*   init: startBit -   0  1 *arrayByte -      sizeArrayByte -     */ void init(byte startBit, byte* arrayByte, byte sizeArrayByte); /*      pushData: sizeBit -     value -   (       ) */ void pushData(byte sizeBit, uint32_t value); /*       popData: return     . */ byte* popData(); protected: byte startBit; //  byte* arrayByte; //   byte sizeArrayByte; //   byte position = 0; //    bool clearFlag = false; //   void setStartBit(byte &value); //     void clearStartBit(byte &value); //     }; 

Avec la méthode d'initialisation, je pense que tout est clair:


 void PSP::init(byte startBit, byte* arrayByte, byte sizeArrayByte) { this->startBit = startBit; this->arrayByte = arrayByte; this->sizeArrayByte = sizeArrayByte; } 

La méthode d'ajout de données est plus compliquée, ici, par des manipulations rudimentaires au niveau du bit, nous plaçons les données dans notre tableau d'octets.


 void PSP::pushData(byte sizeBit, uint32_t value) { byte free; byte y; int remBit = 0; //      //   ,     if (!clearFlag) { for (byte i = 0; i < sizeArrayByte; i++) { arrayByte[i] = 0; } clearFlag = true; } //        7      while (remBit > -1) { free = 7 - (position) % 7; //        y = (position) / 7; //     //       remBit = sizeBit - free; //      if (remBit < 0) { arrayByte[y] |= value << ~remBit + 1; //   ,    position += sizeBit; //        remBit = -1; //      } //      else if (remBit > 0) { arrayByte[y] |= value >> remBit; //     ,    position += sizeBit - remBit; sizeBit = remBit; //        } //         else if (remBit == 0) { arrayByte[y] |= value; //    position += sizeBit; remBit = -1; //      } clearStartBit(arrayByte[y]); //   } setStartBit(arrayByte[0]); //   } 

Méthode pour obtenir un tableau d'octets d'un paquet:


 byte* PSP::popData() { position = 0; //   clearFlag = false; //    return arrayByte; //   } 

Et enfin, quelques fonctions auxiliaires:


 //      void PSP::setStartBit(byte &value) { if (startBit == 0) value &= ~(1 << 7); else value |= 1 << 7; } //      void PSP::clearStartBit(byte &value) { if (startBit == 1) value &= ~(1 << 7); else value |= 1 << 7; } 

Pour résumer


Grâce au travail accompli, le protocole compact de streaming de données PSP1N et les bibliothèques prêtes à l'emploi qui peuvent être téléchargées depuis GitHub ici sont «nés». Dans ce référentiel, vous trouverez:


  1. Exemple d'utilisation de la bibliothèque ColsolePSP1N / C #
  2. PSP1N_CPP / contient la bibliothèque PSP pour travailler avec le protocole et un exemple de son utilisation sur Arduino
  3. PSP1N_CSHARP / bibliothèque de protocoles pour .NET

Pour démontrer le fonctionnement du protocole, vous pouvez flasher l'esquisse dans Arduino et dans l'exemple ExampleSerialPortRead sur l'ordinateur, recevoir des données du microcontrôleur via le port COM. Là, ces données sont décryptées et affichées dans une application console. Je parlerai une fois de plus de la bibliothèque écrite en C # pour le côté récepteur.


TestingConsole:



MISE À JOUR (31/03/19): Modification de l'algorithme d'encodage et de décodage

Source: https://habr.com/ru/post/fr441506/


All Articles