С моими личными мыслями о том, что Structs является наиболее эффективным способом отправки множества различных переменных, я создал библиотеку, чтобы упростить отправку структур и переменных по последовательному каналу. Исходный код
В этой библиотеке это делает отправку через последовательный легко. Я использовал с серийным аппаратным и программным обеспечением. Обычно это используется в сочетании с xbee, поэтому я могу по беспроводной связи отправлять данные в и из робота.
При отправке данных это упрощается, поскольку позволяет отправлять переменную или структуру (это не важно).
Вот пример отправки простого символа через сериал:
// 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');
Пример отправки простого int через сериал:
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');
Пример отправки структуры через серийный номер:
// 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');
Получение примеров:
Получение символа, который был отправлен через 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');
Получение int, отправленного через 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');
Получение структуры, отправленной через 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');
После того, как вы прочитали данные с помощью, StreamSend::receiveObject()
вам нужно знать, были ли данные ХОРОШИ, Не найдены или ПЛОХО.
Хорошо = Успешно
Not Found = Не найдено префиксного символа в указанном ostream
Плохо = Каким-то образом был найден префикс, но данные не повреждены. Обычно это означает, что не было найдено ни одного суффиксного символа или данные были неправильного размера.
Проверка достоверности данных:
// 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:
#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
Если вы действительно хотите отправить его быстро , я рекомендую Full Duplex Serial (FDX). Это тот же протокол, который используют USB и Ethernet, и он намного быстрее, чем UART. Недостатком является то, что обычно требуется внешнее оборудование для обеспечения высокой скорости передачи данных. Я слышал, что новый softwareSreial поддерживает FDX, но это может быть медленнее, чем аппаратный UART. Подробнее о протоколах связи см. Как подключить два Arduino без экранов?
источник
Отправка структуры довольно проста.
Вы можете объявить структуру, как обычно, и затем использовать memcpy (@ myStruct, @ myArray), чтобы скопировать данные в новое местоположение, а затем использовать нечто похожее на приведенный ниже код, чтобы записать данные в виде потока данных.
Затем вы можете прикрепить подпрограмму прерывания к выводу на другом устройстве, которое выполняет следующие действия:
// сказать mcu, чтобы он вызывал fxn при пинхай. Это произойдет практически в любой момент. если это нежелательно, удалите прерывание и просто наблюдайте за новыми персонажами в вашем главном исполнительном цикле (иначе, опрос UART).
Синтаксис и использование указателей потребует некоторого пересмотра. Я потянул всю ночь, так что я уверен, что приведенный выше код даже не скомпилируется, но идея есть. Заполните свою структуру, скопируйте ее, используйте внеполосную сигнализацию, чтобы избежать ошибок кадрирования, запишите данные. С другой стороны, получите данные, скопируйте их в структуру, и затем данные станут доступны через обычные методы доступа членов.
Использование битовых полей также будет работать, просто имейте в виду, что клев будет казаться задом наперед. Например, попытка записать 0011 1101 может привести к появлению 1101 0011 на другом конце, если машины различаются в порядке байтов.
Если важна целостность данных, вы также можете добавить контрольную сумму, чтобы убедиться, что вы не копируете неверно выровненные данные мусора. Это быстрая и эффективная проверка, которую я рекомендую.
источник
Если вы можете терпеть объем данных, отладки communicatons это так намного проще при передаче строк , чем при отправке бинарного; sprintf () / sscanf () и их варианты здесь ваши друзья. Заключите сообщение в выделенные функции в свой собственный модуль (файл .cpp); если вам нужно оптимизировать канал позже - после того, как у вас будет работающая система - вы можете заменить строковый модуль на один кодированный для меньших сообщений.
Вы значительно упростите свою жизнь, если будете строго придерживаться спецификаций протокола передачи и более свободно интерпретировать их при приеме, принимая во внимание ширину поля, разделители, окончания строк, незначительные нули, наличие
+
знаков и т. Д.источник
У меня нет официальных учетных данных, но, по моему опыту, все пошло довольно эффективно, когда я выбрал определенную позицию (и) символов, чтобы содержать состояние переменной, так что вы можете указать первые три символа как температуру, а следующие три как угол сервопривода и тд. На отправляющей стороне я сохранял переменные по отдельности, а затем объединял их в строку для последовательной отправки. На приемном конце я бы выделил строку, получив первые три символа и превратив их в нужный мне тип переменной, а затем сделал бы это снова, чтобы получить следующее значение переменной. Эта система работает лучше всего, когда вы точно знаете, сколько символов будет занимать каждая переменная, и вы всегда будете искать одни и те же переменные (которые, я надеюсь, являются заданными) каждый раз, когда последовательные данные проходят цикл.
Вы можете выбрать одну переменную, чтобы поместить последнюю переменную неопределенной длины, а затем получить эту переменную от ее первого символа до конца строки. Конечно, строка последовательных данных может быть очень длинной в зависимости от типов переменных и их огромного количества, но это система, которую я использую, и пока единственное препятствие, с которым я столкнулся, - это последовательная длина, так что это единственный недостаток, который я знать о.
источник
struct
организована в памяти (без учета заполнения), и я полагаю, что используемые вами функции передачи данных будут аналогичны тем, которые обсуждались в ответе Стивена .Отправить данные структуры через последовательный
Ничего фантастического. Отправляет структуру. Он использует escape-символ «^» для разделения данных.
Arduino код
Код Python:
источник