J'ai besoin d'un protocole de communication simple entre deux appareils (un PC et un microcontrôleur). Le PC doit envoyer certaines commandes et certains paramètres au micro. Le micro doit transmettre un tableau d'octets (données du capteur).
Les données doivent être protégées contre le bruit (en plus du contrôle de parité, je pense que j'ai besoin d'une autre méthode de correction des données).
Existe-t-il une solution standard pour ce faire? (Je n'ai besoin que d'une idée, pas de la solution complète).
P.S. Tout conseil est apprécié. P.P.S Désolé pour toute erreur de grammaire, j'espère que vous comprenez.
Édition 1. Je n'ai pas décidé si ce sera maître/esclave ou les deux côtés peuvent initier la communication. Le PC doit savoir quand micro a fait un travail et peut envoyer des données. Il peut interroger le micro en continu si les données sont prêtes ou le micro peut envoyer des données lorsqu'un travail est terminé. Je ne sais pas ce qui est meilleur et plus simple.
Édition 2. Matériel et couche physique protocole. Depuis RS-232 C standard série utilisé dans le PC, j'utiliserai communication asynchrone . Je n'utiliserai que des signaux RxD, TxD et GND. Je ne peux pas utiliser de fils supplémentaires car le microcontrôleur AFAIK ne les prend pas en charge. BTW J'utilise la puce AVR ATmega128.
Je vais donc utiliser un débit en bauds fixe, 8 bits de données, 2 bits d'arrêt sans contrôle de parité (ou avec?).
protocole de liaison de données. C'est ce qui concernait principalement ma question. Merci d'avoir suggéré les protocoles HDLC , PPP et Modbus . Je vais faire des recherches là-dessus.
J'utiliserais HDLC . J'ai eu de la chance avec ça dans le passé. Je voudrais pour un point à point utiliser simplement le cadrage asynchrone et oublier tous les autres trucs de contrôle car ce serait probablement exagéré.
En plus d'utiliser HDLC pour le cadrage du paquet. Je formate mon paquet comme suit. Voici comment les options sont passées en utilisant 802.11
U8 cmd;
U8 len;
u8 payload[len];
La taille totale de chaque paquet de commandes est len +2
Vous définissez ensuite des commandes comme
#define TRIGGER_SENSOR 0x01
#define SENSOR_RESPONSE 0x02
L'autre avantage est que vous pouvez ajouter de nouvelles commandes et si vous concevez correctement votre analyseur pour ignorer les commandes non définies, vous aurez une certaine compatibilité descendante.
Donc, tout mettre ensemble, le paquet ressemblerait à ce qui suit.
// total packet length minus flags len+4
U8 sflag; //0x7e start of packet end of packet flag from HDLC
U8 cmd; //tells the other side what to do.
U8 len; // payload length
U8 payload[len]; // could be zero len
U16 crc;
U8 eflag; //end of frame flag
Le système surveillera ensuite le flux série pour l'indicateur 0x7e et quand il est là, vous vérifiez la longueur pour voir s'il est pklen> = 4 et pklen = len + 4 et que le crc est valide. Remarque: ne vous fiez pas uniquement à crc pour les petits paquets, vous obtiendrez beaucoup de faux positifs et vérifiez également la longueur. Si la longueur ou le crc ne correspond pas, réinitialisez simplement la longueur et le crc et commencez par décoder la nouvelle image. S'il s'agit d'une correspondance, copiez le paquet dans un nouveau tampon et passez-le à votre fonction de traitement des commandes. Réinitialisez toujours la longueur et le crc lorsqu'un drapeau est reçu.
Pour votre fonction de traitement des commandes, saisissez les cmd et len, puis utilisez un commutateur pour gérer chaque type de commande. J'exige également qu'un certain événement envoie une réponse pour que le système se comporte comme un appel de procédure à distance piloté par un événement.
Ainsi, par exemple, le dispositif capteur peut avoir une minuterie ou répondre à une commande pour effectuer une lecture. Il formaterait alors un paquet et l'enverrait au PC et le PC répondrait qu'il a reçu le paquet. Si ce n'est pas le cas, le capteur peut renvoyer à expiration.
De plus, lorsque vous effectuez un transfert réseau, vous devez le concevoir comme une pile réseau comme les points OSI modle as Foredecker n'oubliez pas les trucs de couche physique . Mon message avec le HDLC est le couche de liaison de données et le RPC et la gestion des commandes est la couche d'application .
Les protocoles RS232 sont délicats. La suggestion d'utiliser HDLC est bonne, mais ce n'est pas la solution complète. Il y a d'autres choses que vous devez décider:
Je vous suggère d'utiliser 8 bits de données, aucune parité matérielle, 1 bit d'arrêt et d'utiliser un contrôle de flux basé sur logiciel. Vous devez utiliser la transmission automatique si votre matériel le prend en charge. Si ce n'est pas le cas, l'autobaud est diaboliquement difficile à faire dans les logiciels.
Il y a de bonnes réponses ici, voici quelques conseils utiles:
Même si vos paquets ne sont pas séparés dans le temps, l'octet de synchronisation est un moyen essentiel de réduire le nombre d'emplacements dont vous avez besoin pour construire un paquet à partir de. Vos appareils devront souvent gérer un tas de données indésirables (c'est-à-dire la fin d'un paquet en vol lors de leur mise sous tension ou suite à une collision matérielle). Sans octet de synchronisation, vous devrez essayer de créer un paquet à partir de chaque octet que vous recevez. L'octet de synchronisation signifie que seulement 1/255 octets de bruit aléatoire pourraient être le premier octet de votre paquet. Aussi FANTASTIQUE lorsque vous voulez espionner votre protocole.
Avoir une adresse sur vos paquets ou même un peu dire maître/esclave ou pc/périphérique est utile lorsque vous regardez les paquets via un outil snoop d'un type ou d'un autre. Vous pouvez le faire en ayant un octet de synchronisation différent pour le PC que le DISPOSITIF. Cela signifie également qu'un appareil ne répondra pas à son propre écho.
Vous voudrez peut-être examiner la correction d'erreurs (comme Hamming ). Vous empaquetez 8 bits de données dans un octet protégé de 12 bits. N'importe lequel de ces 12 bits peut être inversé en cours de route et les 8 bits d'origine récupérés. Utile pour le stockage de données (utilisé sur CD) ou lorsque l'appareil ne peut pas renvoyer facilement (liaisons satellite, RF unidirectionnel).
Les numéros de paquets vous facilitent la vie. Un paquet envoyé porte un numéro, les réponses portent le même numéro et un indicateur disant "réponse". Cela signifie que les paquets qui ne sont jamais arrivés (synchronisation corrompue, par exemple) sont facilement détectés par l'expéditeur et en mode duplex intégral avec une liaison lente, deux commandes peuvent être envoyées avant la réception de la première réponse. Cela facilite également l'analyse de protocole (un tiers peut comprendre quels paquets ont été reçus sans connaître le protocole sous-jacent)
Avoir un seul maître est une simplification impressionnante. Cela dit, dans un environnement en duplex intégral, cela n'a pas beaucoup d'importance. Il suffit de dire que vous devriez toujours le faire, sauf si vous essayez d'économiser de l'énergie ou si vous faites quelque chose d'événement piloté à la fin de l'appareil (état d'entrée modifié, échantillon prêt).
J'ai lu cette question il y a quelques mois, ayant exactement le même problème, et je n'ai pas vraiment trouvé quelque chose d'assez efficace pour un petit micro 8 bits avec de petites quantités de RAM. Tellement inspiré par CAN et LIN, j'ai construit quelque chose pour faire le travail. Je l'ai appelé MIN (Microcontroller Interconnect Network) et je l'ai téléchargé sur GitHub ici:
https://github.com/min-protocol/min
Il y a deux implémentations: une en C intégré, une en Python pour un PC. Plus un petit programme de test "hello world" où le PC envoie des commandes et le firmware allume une LED. J'ai blogué à propos de cela et de le faire fonctionner sur une carte Arduino ici:
https://kentindell.wordpress.com/2015/02/18/micrcontroller-interconnect-network-min-version-1-0/
MIN est assez simple. J'ai corrigé la représentation de la couche 0 (8 bits de données, 1 bit d'arrêt, pas de parité) mais laissé le débit en bauds ouvert. Chaque trame commence par trois octets 0xAA qui en binaire est 1010101010, une belle pulsetrain pour faire la détection de taux d'autobaud si une extrémité veut s'adapter dynamiquement à l'autre. Les trames sont de 0 à 15 octets de charge utile, avec une somme de contrôle Fletcher 16 bits ainsi qu'un octet de contrôle et un identifiant 8 bits (pour indiquer à l'application ce que contiennent les données de charge utile).
Le protocole utilise le bourrage de caractères de sorte que 0xAA 0xAA 0xAA indique toujours le début de la trame. Cela signifie que si un appareil sort de la réinitialisation, il se synchronise toujours avec le début de la trame suivante (un objectif de conception pour MIN n'était jamais de laisser passer une trame incomplète ou incorrecte). Cela signifie également qu'il n'est pas nécessaire d'avoir des contraintes de synchronisation spécifiques entre les octets et entre les trames. Tous les détails du protocole sont dans le wiki de repo GitHub.
Il y a place pour de futures améliorations avec MIN. J'ai laissé des crochets pour le passage des messages de blocage (4 bits de l'octet de contrôle sont réservés) et pour la négociation de niveau supérieur des capacités (l'identifiant 0xFF est réservé), donc il y a beaucoup de place pour ajouter la prise en charge des fonctionnalités couramment requises.
Ma suggestion est Modbus. Il s'agit d'un protocole standard simple et efficace pour la communication avec des appareils dotés de capteurs et de paramètres (par exemple un API). Vous pouvez obtenir les spécifications sur http://www.modbus.org . Il existe depuis 1979 et gagne en popularité, vous n'aurez aucun problème à trouver des exemples et des bibliothèques.
Voici un protocole alternatif:
u8 Sync // A constant value which always marks the start of a packet
u16 Length // Number of bytes in payload
u8 Data[Length] // The payload
u16 Crc // CRC
Utilisez RS232/UART, car le PC (port série) et le processeur (UART) peuvent déjà gérer cela avec un minimum d'agitation (il suffit d'avoir une puce MAX232 ou similaire pour effectuer le changement de niveau).
Et en utilisant RS232/UART, vous n'avez pas à vous soucier du maître/esclave si ce n'est pas pertinent. Le contrôle de flux est disponible si nécessaire.
Logiciel PC suggéré: écrivez le vôtre ou Docklight pour une surveillance et un contrôle simples (la version d'évaluation est gratuite).
Pour une meilleure vérification des erreurs, la plus simple est la vérification de la parité, ou si vous avez besoin de quelque chose de plus puissant, peut-être codage convolutionnel .
Dans tous les cas, quoi que vous fassiez: restez simple!
EDIT: L'utilisation de RS232 avec un PC est encore plus facile qu'auparavant, car vous pouvez désormais obtenir des convertisseurs USB vers RS232/TTL. Une extrémité entre dans la prise USB de votre PC et apparaît comme un port série normal; l'autre produit des signaux 5 V ou 3,3 V qui peuvent être connectés directement à votre processeur, sans changement de niveau requis.
Nous avons utilisé TTL-232R-3V de FDTI Chip, qui fonctionne parfaitement pour ce type d'application.
Ma seule suggestion est que si vous avez besoin de résistance au bruit, vous voudrez peut-être utiliser le RS-422/485 en duplex intégral. Vous pouvez utiliser un IC similaire à this du côté AVR, puis un convertisseur RS-232-> RS-422 du côté PC comme le 485PTBR ici . Si vous pouvez trouver ou fabriquer un câble blindé (deux paires blindées torsadées), vous aurez encore plus de protection. Et tout cela est invisible pour le micro et le PC - aucun changement de logiciel.
Quoi que vous fassiez, assurez-vous que vous utilisez un système duplex intégral et assurez-vous que les lignes d'activation de lecture/écriture sont activées sur le CI.
Vous pouvez voir Telemetry
et son implémentation de bureau associée dans python Pytelemetry
Il s'agit d'un protocole basé sur PubSub, mais contrairement à MQTT, il s'agit d'un protocole point à point, pas de courtier .
Comme tout protocole pubsub, vous pouvez publier d'une extrémité sur un topic
et être notifié à l'autre extrémité sur ce sujet.
Du côté intégré, la publication sur un sujet est aussi simple que:
publish("someTopic","someMessage")
Pour les chiffres:
publish_f32("foo",1.23e-4)
publish_u32("bar",56789)
Cette façon d'envoyer des variables peut sembler limitée, mais le prochain jalon a l'intention d'ajouter une signification supplémentaire à l'analyse du sujet en faisant des choses comme ceci:
// Add an indexing meaning to the topic
publish("foo:1",45) // foo with index = 1
publish("foo:2",56) // foo with index = 2
// Add a grouping meaning to the topic
publish("bar/foo",67) // foo is under group 'bar'
// Combine
publish("bar/foo:45",54)
C'est bien si vous devez envoyer des tableaux, des structures de données complexes, etc.
En outre, le modèle PubSub est excellent en raison de sa flexibilité. Vous pouvez créer des applications maître/esclave, appareil à appareil, etc.
La bibliothèque C est très simple à ajouter sur n'importe quel nouvel appareil tant que vous avez une bibliothèque UART UTC décente dessus).
Il vous suffit d'instancier une structure de données appelée TM_transport
(défini par Telemetry
) et affectez les 4 pointeurs de fonction read
readable
write
writeable
.
// your device's uart library function signatures (usually you already have them)
int32_t read(void * buf, uint32_t sizeToRead);
int32_t readable();
int32_t write(void * buf, uint32_t sizeToWrite);
int32_t writeable();
Pour utiliser la télémétrie, il vous suffit d'ajouter le code suivant
// At the beginning of main function, this is the ONLY code you have to add to support a new device with telemetry
TM_transport transport;
transport.read = read;
transport.write = write;
transport.readable = readable;
transport.writeable = writeable;
// Init telemetry with the transport structure
init_telemetry(&transport);
// and you're good to start publishing
publish_i32("foobar",...
Côté bureau, il y a le module pytelemetry
qui implémente le protocole.
Si vous connaissez python, le code suivant se connecte à un port série, publie une fois sur le sujet foo
, imprime tous les sujets reçus pendant 3 secondes puis se termine.
import runner
import pytelemetry.pytelemetry as tm
import pytelemetry.transports.serialtransport as transports
import time
transport = transports.SerialTransport()
telemetry = tm.pytelemetry(transport)
app = runner.Runner(transport,telemetry)
def printer(topic, data):
print(topic," : ", data)
options = dict()
options['port'] = "COM20"
options['baudrate'] = 9600
app.connect(options)
telemetry.subscribe(None, printer)
telemetry.publish('bar',1354,'int32')
time.sleep(3)
app.terminate()
Si vous ne connaissez pas python, vous pouvez utiliser l'interface de ligne de commande
La ligne de commande peut être démarrée avec
pytlm
Ensuite, vous pouvez connect
, ls
(liste) les sujets reçus, print
les données reçues sur un sujet, pub
(publier) sur un sujet, ou ouvrir un plot
sur un sujet pour afficher les données reçues en temps réel
En ce qui concerne les contrôles de parité (comme cela est apparu plusieurs fois ici):
Ils sont pour la plupart inutiles. Si vous craignez qu'un seul bit puisse être modifié par erreur, il est fort probable qu'un deuxième bit puisse également changer et vous obtiendrez un faux positif du contrôle de parité.
Utilisez quelque chose de léger comme CRC16 avec une table de recherche - il peut être calculé à mesure que chaque octet est reçu et n'est en fait qu'un XOR. La suggestion de Steve Melnikoff est idéale pour les petits micros.
Je suggérerais également de transmettre des données lisibles par l'homme, plutôt que des données binaires brutes (si la performance n'est pas votre première priorité). Cela rendra le débogage et les fichiers journaux beaucoup plus agréables.
peut-être que cette question peut être complètement stupide, mais quelqu'un a-t-il envisagé d'utiliser l'un des protocoles X/Y/Z MODEM ?
Le principal avantage de l'utilisation de l'un des protocoles ci-dessus est la grande disponibilité d'implémentations prêtes à l'emploi dans divers environnements de programmation.
Vous ne spécifiez pas exactement comment se comporte le microcontrôleur, mais est-ce que tout ce qui est transmis par le micro sera une réponse directe à une commande du PC? Si c'est le cas, il semble que vous pouvez utiliser un protocole maître/esclave d'une certaine sorte (ce sera généralement la solution la plus simple). Si les deux parties peuvent initier la communication, vous avez besoin d'un protocole de couche de liaison de données plus général. HDLC est un protocole classique pour cela. Bien que le protocole complet soit probablement exagéré pour vos besoins, vous pouvez par exemple au moins utiliser le même format de trame. Vous pouvez également jeter un œil à PPP pour voir s'il y a quelque chose d'utile.