Con mis pensamientos personales sobre Structs como la forma más eficiente de enviar muchas variables diferentes, he construido una biblioteca para ayudar a facilitar el envío de estructuras y variables en serie. Código fuente
En esta biblioteca, hace que el envío sea en serie fácilmente. He usado con hardware y software en serie. Por lo general, esto se usa en conjunto con xbee para que pueda enviar de forma inalámbrica los datos hacia y desde el robot.
Cuando envíe datos, hágalo simple, ya que le permite enviar una variable o una estructura (no le importa).
Aquí hay un ejemplo de envío de un simple char sobre el serial:
// 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');
Ejemplo de enviar un int simple sobre el serial:
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');
Ejemplo de envío de una estructura en serie:
// 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');
Recibiendo ejemplos:
Al recibir un char que se envió a través de Streamsend:
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');
Al recibir un int que se envió a través de StreamSend:
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');
Al recibir un Struct que se envió a través de StreamSend:
// 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');
Una vez que lea los datos utilizando StreamSend::receiveObject()
, necesita saber si los datos fueron BUENOS, No encontrados o MALOS.
Bueno = exitoso
No encontrado = No se encontró ningún carácter de prefijo en el ostream especificado
Malo = De alguna manera se encontró un carácter de prefijo, pero los datos no están intactos. Por lo general, significa que no se encontraron caracteres de sufijo o que los datos no tenían el tamaño correcto.
Validez de prueba de datos:
// 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
}
Clase SteamSend:
#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
Si realmente desea enviarlo rápido , le recomiendo la serie Full Duplex (FDX). Es el mismo protocolo que utilizan USB y Ethernet, y es mucho más rápido que UART. La desventaja es que generalmente requiere hardware externo para facilitar las altas velocidades de datos. He oído que el nuevo softwareSreial es compatible con FDX, pero esto puede ser más lento incluso que el hardware UART. Para obtener más información sobre los protocolos de comunicación, consulte ¿Cómo conectar dos Arduino sin escudos?
fuente
Enviar una estructura es bastante simple.
Puede declarar la estructura como lo haría normalmente, y luego usar memcpy (@ myStruct, @ myArray) para copiar los datos en una nueva ubicación, y luego usar algo similar al código siguiente para escribir los datos como flujo de datos.
Luego puede adjuntar una rutina de interrupción al pin en el otro dispositivo que hace lo siguiente:
// dile a mcu que llame a fxn cuando pinhigh. Esto sucederá en prácticamente cualquier momento. si eso no se desea, elimine la interrupción y simplemente busque nuevos personajes en su bucle ejecutivo principal (también conocido como sondeo UART).
La sintaxis y el uso de punteros necesitarán alguna revisión. Estuve toda la noche, así que estoy seguro de que el código anterior ni siquiera se compilará, pero la idea está ahí. Rellene su estructura, cópiela, utilice la señalización fuera de banda para evitar errores de trama, escriba los datos. En el otro extremo, reciba los datos, cópielos en una estructura y luego los datos serán accesibles a través de métodos normales de acceso de miembros.
El uso de bitfields también funcionará, solo tenga en cuenta que los mordiscos parecerán estar al revés. Por ejemplo, al intentar escribir 0011 1101, puede aparecer 1101 0011 en el otro extremo si las máquinas difieren en orden de bytes.
Si la integridad de los datos es importante, también puede agregar una suma de verificación para asegurarse de que no está copiando datos basura desalineados. Este es un control rápido y efectivo que recomiendo.
fuente
Si usted puede tolerar el volumen de datos, depuración Communicatons es por lo tanto más fácil al enviar cadenas que al enviar binaria; sprintf () / sscanf () y sus variantes son tus amigos aquí. Adjunte la comunicación en funciones dedicadas en su propio módulo (archivo .cpp); si necesita optimizar el canal más adelante, después de tener un sistema en funcionamiento, puede reemplazar el módulo basado en cadenas por uno codificado para mensajes más pequeños.
Hará su vida mucho más fácil si se atiene a las especificaciones de protocolo en la transmisión e interpreta más libremente en la recepción, en relación con los anchos de campo, delimitadores, terminaciones de línea, ceros insignificantes, presencia de
+
signos, etc.fuente
No tengo credenciales oficiales aquí, pero según mi experiencia, las cosas se han vuelto bastante eficientes cuando elijo ciertas posiciones de caracteres para contener el estado de una variable, de modo que pueda designar los primeros tres caracteres como la temperatura, y el siguiente tres como el ángulo de un servo, y así sucesivamente. En el extremo de envío, guardaría las variables individualmente y luego las combinaría en una cadena para enviar en serie. En el extremo de recepción, separaría la cadena, obtendría los primeros tres caracteres y los convertiría en cualquier tipo de variable que necesite, y luego lo haría nuevamente para obtener el siguiente valor de variable. Este sistema funciona mejor cuando se sabe con certeza la cantidad de caracteres que ocupará cada variable, y siempre se buscan las mismas variables (lo cual espero sea un hecho) cada vez que los datos en serie se repiten.
Puede elegir una variable para colocar la última de longitud indeterminada y luego obtener esa variable desde su primer carácter hasta el final de la cadena. De acuerdo, la cadena de datos en serie podría ser muy larga dependiendo de los tipos de variables y la gran cantidad de ellos, pero este es el sistema que uso y hasta ahora el único inconveniente que he encontrado es la longitud de serie, por lo que esa es la única desventaja que tengo. saber de.
fuente
struct
se organiza a en la memoria (sin tener en cuenta el relleno) e imagino que las funciones de transferencia de datos que usa serán similares a las que se analizan en la respuesta de Steven .Enviar datos de estructura a través de serie
Nada sofisticado. Envía una estructura. Utiliza un carácter de escape '^' para delimitar los datos.
Código Arduino
Código de Python:
fuente