Da meine persönlichen Gedanken zu Structs die effizienteste Methode zum Senden vieler verschiedener Variablen sind, habe ich eine Bibliothek erstellt, die das Senden von Structs und Variablen über die serielle Schnittstelle erleichtert. Quellcode
In dieser Bibliothek macht es das Senden durch serielle leicht. Ich habe mit mit Hardware und Software Serien verwendet. Normalerweise wird dies in Verbindung mit xbee verwendet, damit ich die Daten drahtlos zum und vom Roboter senden kann.
Beim Senden von Daten wird dies vereinfacht, da Sie entweder eine Variable oder eine Struktur senden können (das ist ihm egal).
Hier ist ein Beispiel für das Senden eines einfachen Zeichens über die Seriennummer:
// Send the variable charVariable over the serial.
// To send the variable you need to pass an instance of the Serial to use,
// a reference to the variable to send, and the size of the variable being sent.
// If you would like you can specify 2 extra arguments at the end which change the
// default prefix and suffix character used when attempting to reconstruct the variable
// on the receiving end. If prefix and suffix character are specified they'll need to
// match on the receiving end otherwise data won't properly be sent across
char charVariable = 'c'; // Define the variable to be sent over the serial
StreamSend::sendObject(Serial, &charVariable, sizeof(charVariable));
// Specify a prefix and suffix character
StreamSend::sendObject(Serial, &charVariable, sizeof(charVariable), 'a', 'z');
Beispiel für das Senden einer einfachen Ganzzahl über die Seriennummer:
int intVariable = 13496; // Define the int to be sent over the serial
StreamSend::sendObject(xbeeSerial, &intVariable, sizeof(intVariable));
// Specify a prefix and suffix character
StreamSend::sendObject(xbeeSerial, &intVariable, sizeof(intVariable), 'j', 'p');
Beispiel für das Senden einer Struktur über serielle:
// Define the struct to be sent over the serial
struct SIMPLE_STRUCT {
char charVariable;
int intVariable[7];
boolean boolVariable;
};
SIMPLE_STRUCT simpleStruct;
simpleStruct.charVariable = 'z'; // Set the charVariable in the struct to z
// Fill the intVariable array in the struct with numbers 0 through 6
for(int i=0; i<7; i++) {
simpleStruct.intVariable[i] = i;
}
// Send the struct to the object xbeeSerial which is a software serial that was
// defined. Instead of using xbeeSerial you can use Serial which will imply the
// hardware serial, and on a Mega you can specify Serial, Serial1, Serial2, Serial3.
StreamSend::sendObject(xbeeSerial, &simpleStruct, sizeof(simpleStruct));
// Send the same as above with a different prefix and suffix from the default values
// defined in StreamSend. When specifying when prefix and suffix character to send
// you need to make sure that on the receiving end they match otherwise the data
// won't be able to be read on the other end.
StreamSend::sendObject(xbeeSerial, &simpleStruct, sizeof(simpleStruct), '3', 'u');
Empfang Beispiele:
Empfangen eines Zeichens, das über Streamsend gesendet wurde:
char charVariable; // Define the variable on where the data will be put
// Read the data from the Serial object an save it into charVariable once
// the data has been received
byte packetResults = StreamSend::receiveObject(Serial, &charVariable, sizeof(charVariable));
// Reconstruct the char coming from the Serial into charVariable that has a custom
// suffix of a and a prefix of z
byte packetResults = StreamSend::receiveObject(Serial, &charVariable, sizeof(charVariable), 'a', 'z');
Empfangen eines Int, der über StreamSend gesendet wurde:
int intVariable; // Define the variable on where the data will be put
// Reconstruct the int from xbeeSerial into the variable intVariable
byte packetResults = StreamSend::receiveObject(xbeeSerial, &intVariable, sizeof(intVariable));
// Reconstruct the data into intVariable that was send with a custom prefix
// of j and a suffix of p
byte packetResults = StreamSend::receiveObject(xbeeSerial, &intVariable, sizeof(intVariable), 'j', 'p');
Empfangen einer Struktur, die über StreamSend gesendet wurde:
// Define the struct that the data will be put
struct SIMPLE_STRUCT {
char charVariable;
int intVariable[7];
boolean boolVariable;
};
SIMPLE_STRUCT simpleStruct; // Create a struct to store the data in
// Reconstruct the data from xbeeSerial into the object simpleStruct
byte packetResults = StreamSend::receiveObject(xbeeSerial, &simpleStruct, sizeof(simpleStruct));
// Reconstruct the data from xbeeSerial into the object simplestruct that has
// a prefix of 3 and a suffix of p
byte packetResults = StreamSend::receiveObject(xbeeSerial, &simpleStruct, sizeof(simpleStruct), '3', 'p');
Wenn Sie die Daten mit gelesen haben, müssen StreamSend::receiveObject()
Sie wissen, ob die Daten GUT, Nicht gefunden oder SCHLECHT waren.
Gut = erfolgreich
Nicht gefunden = Im angegebenen ostream wurde kein Präfixzeichen gefunden
Schlecht = Irgendwie wurde ein Präfixzeichen gefunden, aber die Daten sind nicht intakt. Normalerweise bedeutet dies, dass kein Suffixzeichen gefunden wurde oder die Daten nicht die richtige Größe hatten.
Gültigkeit der Daten prüfen:
// Once you call StreamSend::receiveObject() it returns a byte of the status of
// how things went. If you run that though some of the testing functions it'll
// let you know how the transaction went
if(StreamSend::isPacketGood(packetResults)) {
//The Packet was Good
} else {
//The Packet was Bad
}
if(StreamSend::isPacketCorrupt(packetResults)) {
//The Packet was Corrupt
} else {
//The Packet wasn't found or it was Good
}
if(StreamSend::isPacketNotFound(packetResults)) {
//The Packet was not found after Max # of Tries
} else {
//The Packet was Found, but can be corrupt
}
SteamSend-Klasse:
#include "Arduino.h"
#ifndef STREAMSEND_H
#define STREAMSEND_H
#define PACKET_NOT_FOUND 0
#define BAD_PACKET 1
#define GOOD_PACKET 2
// Set the Max size of the Serial Buffer or the amount of data you want to send+2
// You need to add 2 to allow the prefix and suffix character space to send.
#define MAX_SIZE 64
class StreamSend {
private:
static int getWrapperSize() { return sizeof(char)*2; }
static byte receiveObject(Stream &ostream, void* ptr, unsigned int objSize, unsigned int loopSize);
static byte receiveObject(Stream &ostream, void* ptr, unsigned int objSize, unsigned int loopSize, char prefixChar, char suffixChar);
static char _prefixChar; // Default value is s
static char _suffixChar; // Default value is e
static int _maxLoopsToWait;
public:
static void sendObject(Stream &ostream, void* ptr, unsigned int objSize);
static void sendObject(Stream &ostream, void* ptr, unsigned int objSize, char prefixChar, char suffixChar);
static byte receiveObject(Stream &ostream, void* ptr, unsigned int objSize);
static byte receiveObject(Stream &ostream, void* ptr, unsigned int objSize, char prefixChar, char suffixChar);
static boolean isPacketNotFound(const byte packetStatus);
static boolean isPacketCorrupt(const byte packetStatus);
static boolean isPacketGood(const byte packetStatus);
static void setPrefixChar(const char value) { _prefixChar = value; }
static void setSuffixChar(const char value) { _suffixChar = value; }
static void setMaxLoopsToWait(const int value) { _maxLoopsToWait = value; }
static const char getPrefixChar() { return _prefixChar; }
static const char getSuffixChar() { return _suffixChar; }
static const int getMaxLoopsToWait() { return _maxLoopsToWait; }
};
//Preset Some Default Variables
//Can be modified when seen fit
char StreamSend::_prefixChar = 's'; // Starting Character before sending any data across the Serial
char StreamSend::_suffixChar = 'e'; // Ending character after all the data is sent
int StreamSend::_maxLoopsToWait = -1; //Set to -1 for size of current Object and wrapper
/**
* sendObject
*
* Converts the Object to bytes and sends it to the stream
*
* @param Stream to send data to
* @param ptr to struct to fill
* @param size of struct
* @param character to send before the data stream (optional)
* @param character to send after the data stream (optional)
*/
void StreamSend::sendObject(Stream &ostream, void* ptr, unsigned int objSize) {
sendObject(ostream, ptr, objSize, _prefixChar, _suffixChar);
}
void StreamSend::sendObject(Stream &ostream, void* ptr, unsigned int objSize, char prefixChar, char suffixChar) {
if(MAX_SIZE >= objSize+getWrapperSize()) { //make sure the object isn't too large
byte * b = (byte *) ptr; // Create a ptr array of the bytes to send
ostream.write((byte)prefixChar); // Write the suffix character to signify the start of a stream
// Loop through all the bytes being send and write them to the stream
for(unsigned int i = 0; i<objSize; i++) {
ostream.write(b[i]); // Write each byte to the stream
}
ostream.write((byte)suffixChar); // Write the prefix character to signify the end of a stream
}
}
/**
* receiveObject
*
* Gets the data from the stream and stores to supplied object
*
* @param Stream to read data from
* @param ptr to struct to fill
* @param size of struct
* @param character to send before the data stream (optional)
* @param character to send after the data stream (optional)
*/
byte StreamSend::receiveObject(Stream &ostream, void* ptr, unsigned int objSize) {
return receiveObject(ostream, ptr, objSize, _prefixChar, _suffixChar);
}
byte StreamSend::receiveObject(Stream &ostream, void* ptr, unsigned int objSize, char prefixChar, char suffixChar) {
return receiveObject(ostream, ptr, objSize, 0, prefixChar, suffixChar);
}
byte StreamSend::receiveObject(Stream &ostream, void* ptr, unsigned int objSize, unsigned int loopSize, char prefixChar, char suffixChar) {
int maxLoops = (_maxLoopsToWait == -1) ? (objSize+getWrapperSize()) : _maxLoopsToWait;
if(loopSize >= maxLoops) {
return PACKET_NOT_FOUND;
}
if(ostream.available() >= (objSize+getWrapperSize())) { // Packet meets minimum size requirement
if(ostream.read() != (byte)prefixChar) {
// Prefix character is not found
// Loop through the code again reading the next char
return receiveObject(ostream, ptr, objSize, loopSize+1, prefixChar, suffixChar);
}
char data[objSize]; //Create a tmp char array of the data from Stream
ostream.readBytes(data, objSize); //Read the # of bytes
memcpy(ptr, data, objSize); //Copy the bytes into the struct
if(ostream.read() != (byte)suffixChar) {
//Suffix character is not found
return BAD_PACKET;
}
return GOOD_PACKET;
}
return PACKET_NOT_FOUND; //Prefix character wasn't found so no packet detected
}
boolean StreamSend::isPacketNotFound(const byte packetStatus) {
return (packetStatus == PACKET_NOT_FOUND);
}
boolean StreamSend::isPacketCorrupt(const byte packetStatus) {
return (packetStatus == BAD_PACKET);
}
boolean StreamSend::isPacketGood(const byte packetStatus) {
return (packetStatus == GOOD_PACKET);
}
#endif
Wenn Sie es wirklich schnell senden wollten , empfehle ich Full Duplex Serial (FDX). Es ist dasselbe Protokoll, das USB und Ethernet verwenden, und es ist viel schneller als UART. Der Nachteil ist, dass normalerweise externe Hardware erforderlich ist, um die hohen Datenraten zu ermöglichen. Ich habe gehört, dass die neue SoftwareSreial FDX unterstützt, dies kann jedoch langsamer sein als die Hardware-UART. Weitere Informationen zu Kommunikationsprotokollen finden Sie unter So verbinden Sie zwei Arduino ohne Abschirmungen?
quelle
Das Senden einer Struktur ist ziemlich einfach.
Sie können die Struktur wie gewohnt deklarieren und dann memcpy (@ myStruct, @ myArray) verwenden, um die Daten an einen neuen Speicherort zu kopieren. Verwenden Sie dann den folgenden Code, um die Daten als Datenstrom zu schreiben.
Anschließend können Sie eine Interruptroutine an den Pin des anderen Geräts anhängen, die folgende Aktionen ausführt:
// sage der mcu, dass sie fxn aufruft, wenn sie scharf ist. Dies wird praktisch jeden Moment passieren. Wenn dies nicht gewünscht ist, entfernen Sie den Interrupt und suchen Sie einfach nach neuen Charakteren in Ihrer Haupt-Executive-Schleife (auch bekannt als UART-Polling).
Syntax und Verwendung von Zeigern müssen überprüft werden. Ich habe einen All-Nighter gezogen, daher bin ich sicher, dass der obige Code nicht einmal kompiliert werden kann, aber die Idee ist da. Füllen Sie Ihre Struktur, kopieren Sie sie, verwenden Sie Außerbandsignale, um Rahmenfehler zu vermeiden, und schreiben Sie die Daten. Empfangen Sie die Daten auf der anderen Seite, kopieren Sie sie in eine Struktur, und dann können Sie über normale Member-Zugriffsmethoden auf die Daten zugreifen.
Die Verwendung von Bitfeldern funktioniert auch, beachten Sie jedoch, dass die Knabbereien rückwärts zu sein scheinen. Beispielsweise kann der Versuch, 0011 1101 zu schreiben, dazu führen, dass 1101 0011 am anderen Ende angezeigt wird, wenn sich die Maschinen in der Bytereihenfolge unterscheiden.
Wenn Datenintegrität wichtig ist, können Sie auch eine Prüfsumme hinzufügen, um sicherzustellen, dass Sie keine falsch ausgerichteten Abfalldaten kopieren. Dies ist eine schnelle und effektive Überprüfung, die ich empfehle.
quelle
Wenn Sie das Datenvolumen tolerieren können, ist das Debuggen von Kommunikationen beim Senden von Zeichenfolgen so viel einfacher als beim Senden von Binärdateien. sprintf () / sscanf () und ihre Varianten sind deine Freunde hier. Schließen Sie die Kommunikation in dedizierten Funktionen in einem eigenen Modul (CPP-Datei) ein. Wenn Sie den Kanal später optimieren müssen - danach Sie ein funktionierendes System haben -, können Sie das auf Zeichenfolgen basierende Modul durch ein Modul ersetzen, das für kleinere Nachrichten codiert ist.
Sie werden Ihr Leben leichter machen, wenn Sie die Protokollspezifikationen beim Senden festhalten und sie beim Empfang lockerer interpretieren, in Bezug auf Feldbreiten, Begrenzer, Zeilenenden, unbedeutende Nullen, Vorhandensein von
+
Zeichen usw.quelle
Ich habe hier keine offiziellen Anmeldeinformationen, aber meiner Erfahrung nach hat es sich als ziemlich effizient erwiesen, wenn ich bestimmte Zeichenpositionen auswähle, die den Status einer Variablen enthalten, sodass Sie die ersten drei Zeichen als Temperatur und das nächste Zeichen festlegen können drei als der Winkel eines Servos und so weiter. Auf der Sendeseite würde ich die Variablen einzeln speichern und sie dann zu einer Zeichenfolge kombinieren, um sie seriell zu senden. Am empfangenden Ende würde ich die Zeichenkette auseinander nehmen lassen, die ersten drei Zeichen erhalten und sie in den von mir benötigten Variablentyp umwandeln, um dann den nächsten Variablenwert zu erhalten. Dieses System funktioniert am besten, wenn Sie genau wissen, wie viele Zeichen jede Variable aufnehmen wird, und Sie bei jedem Durchlaufen der seriellen Daten immer nach denselben Variablen suchen (was hoffentlich eine Selbstverständlichkeit ist).
Sie können eine Variable auswählen, um die letzte Variable von unbestimmter Länge zu setzen, und diese Variable dann vom ersten Zeichen bis zum Ende der Zeichenfolge abrufen. Zugegeben, der serielle Datenstring kann je nach Variablentyp und Anzahl der Variablen sehr lang werden, aber dies ist das System, das ich verwende, und der einzige Nachteil, den ich getroffen habe, ist die serielle Länge. Das ist also der einzige Nachteil, den ich habe kennen.
quelle
struct
im Speicher organisiert ist (ohne Berücksichtigung der Auffüllung), und ich stelle mir vor, dass die von Ihnen verwendeten Datenübertragungsfunktionen denen in Stevens Antwort beschriebenen ähnlich sind .Senden Sie Strukturdaten über serielle
Nichts Besonderes. Sendet eine Struktur. Es verwendet ein Escapezeichen '^', um die Daten einzugrenzen.
Arduino-Code
Python-Code:
quelle