Отправка больших объемов последовательных данных

Поэтому в области робототехники иногда вам нужно несколько плат и /или компьютеров, связанных друг с другом, для обмена информацией или сохранения статистических данных. В настоящее время мне нужно отправить несколько разных переменных по последовательному соединению и задалось вопросом, как лучше всего это сделать?

До сих пор я решил, что отправка структур, вероятно, будет более простым способом отправки данных. Кто-нибудь знает какой-либо другой способ, который может быть более эффективным?

Пожалуйста, имейте в виду, что мне по существу придется отправлять данные для 4 двигателей, компрессоров, разных температур, случайных вещей и трех секций руки.

12 голосов | спросил Steven10172 14 FebruaryEurope/MoscowbFri, 14 Feb 2014 11:58:54 +0400000000amFri, 14 Feb 2014 11:58:54 +040014 2014, 11:58:54

6 ответов


9

С моими личными соображениями о том, что 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() вам нужно знать, были ли данные «ХОРОШО», «Не найдено» или «БАД».

Хорошо = Успешно

Не найдено = префиксный символ не найден в указанном 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
ответил Steven10172 14 FebruaryEurope/MoscowbFri, 14 Feb 2014 11:58:54 +0400000000amFri, 14 Feb 2014 11:58:54 +040014 2014, 11:58:54
1

Если вы действительно хотите отправить его fast , я рекомендую Full Duplex Serial (FDX). Это тот же протокол, что и USB и ethernet, и это намного быстрее, чем UART. Недостатком является то, что для обеспечения высокой скорости передачи данных обычно требуется внешнее оборудование. Я слышал, что новый softwareSreial поддерживает FDX, но это может быть медленнее, чем аппаратное UART. Подробнее о протоколах связи см. Как подключить два Arduino без экранов?

ответил TheDoctor 14 FebruaryEurope/MoscowbFri, 14 Feb 2014 18:20:14 +0400000000pmFri, 14 Feb 2014 18:20:14 +040014 2014, 18:20:14
1

Отправка структуры довольно проста.

Вы можете объявить структуру как обычно, а затем использовать memcpy (@ myStruct, @ myArray), чтобы скопировать данные в новое место, а затем использовать что-то похожее на приведенный ниже код для записи данных в виде потока данных.

unsigned char myArraySender[##];   //make ## large enough to fit struct
memcpy(&myStruct,&myArraySender);  //copy raw data from struct to the temp array
digitalWrite(frameStartPin,High);  //indicate to receiver that data is coming
serial.write(sizeof myStruct);     //tell receiver how many bytes to rx
Serial.write(&myArraySender,sizeof myStruct);   //write bytes
digitalWrite)frameStartPin,Low);   //done indicating transmission 

Затем вы можете подключить подпрограмму прерывания к выходу на другом устройстве, которое выполняет следующие действия:

volatile unsigned char len, tempBuff[##];   
//volatile because the interrupt will not happen at predictable intervals.

attachInterrupt(0,readSerial,Rising);  

//скажите mcu, чтобы вызвать fxn, когда pinhigh. Это произойдет практически в любой момент. если это нежелательно, удалите прерывание и просто следите за новыми символами в вашем основном исполнительном цикле (например, опросе UART).

void readSerial(unsigned char *myArrayReceiver){
    unsigned char tempbuff[sizeof myArrayReceiver];
    while (i<(sizeof myArrayReceiver)) tempBuff[i]=Serial.read();
    memcpy(&tempbuff,&myArrayReceiver);
    Serial.flush();
}

Синтаксис и использование указателей потребует некоторого обзора. Я вытащил все-ноль, поэтому я уверен, что вышеприведенный код даже не будет компилироваться, но идея есть. Заполните свою структуру, скопируйте ее, используйте внеполосную сигнализацию, чтобы избежать ошибок кадрирования, записать данные. С другой стороны, получите данные, скопируйте их в структуру, а затем данные станут доступными с помощью обычных методов доступа членов.

Использование битполей также будет работать, просто имейте в виду, что полубайты будут отображаться в обратном порядке. Например, попытка написать 0011 1101 может привести к появлению 1101 0011 на другом конце, если машины отличаются порядком байтов.

Если целостность данных важна, вы также можете добавить контрольную сумму, чтобы убедиться, что вы не копируете несортированные данные мусора. Это быстрая и эффективная проверка, которую я рекомендую.

ответил 80HD 15 FebruaryEurope/MoscowbSat, 15 Feb 2014 09:09:45 +0400000000amSat, 15 Feb 2014 09:09:45 +040014 2014, 09:09:45
1

Если вы можете терпеть объем данных, отладка коммуникаций так намного проще при отправке строк, чем при отправке двоичных файлов; sprintf () /sscanf () и их варианты здесь ваши друзья. Заключить связь в выделенных функциях в своем собственном модуле (файл .cpp); если вам нужно оптимизировать канал позже - после , у вас есть рабочая система - вы можете заменить строковый модуль одним закодированным для более мелких сообщений.

Вы упростите свою жизнь, если будете придерживаться ограничений по спецификации протокола при передаче и интерпретировать их более свободно при приеме, учитывая ширину поля, разделители, окончания строк, незначительные нули, наличие + знаки и т. д.

ответил JRobert 3 MaramMon, 03 Mar 2014 02:57:16 +04002014-03-03T02:57:16+04:0002 2014, 02:57:16
0

У меня нет официальных верительных грамот здесь, но по моему опыту все прошло довольно эффективно, когда я выбираю определенную позицию (ы) персонажа, чтобы содержать состояние переменной, чтобы вы могли обозначить первые три символа как температуру, а следующие три - как угол сервопривода и т. д. На отправляющем конце я сохранил переменные по отдельности, а затем объединил их в строку для отправки по очереди. На принимающей стороне я бы выделил строку, получив первые три символа и превратив их в любой тип переменной, который мне нужен, а затем повторю это, чтобы получить следующее значение переменной. Эта система работает лучше всего, когда вы точно знаете количество символов, которое будет обрабатывать каждая переменная, и вы всегда будете искать те же самые переменные (которые, я надеюсь, являются заданными) каждый раз, когда последовательные данные пересекаются.

Вы можете выбрать одну переменную, чтобы поставить последнюю неопределенную длину, а затем получить эту переменную от ее первого символа до конца строки. Конечно, строка последовательных данных могла бы стать очень длинной в зависимости от типов переменных и их большого количества, но это система, которую я использую, и до сих пор единственным ударом, который я ударил, является последовательная длина, так что это единственный недостаток I знать.

ответил Newbie97 15 FebruaryEurope/MoscowbSat, 15 Feb 2014 08:23:43 +0400000000amSat, 15 Feb 2014 08:23:43 +040014 2014, 08:23:43
0

Отправить данные структуры через последовательный

Ничего необычного. Отправляет структуру. Он использует escape-символ '^' для разграничения данных.

Код Arduino

typedef struct {
 float ax1;
 float ay1;
 float az1;
 float gx1;
 float gy1;
 float gz1;
 float ax2;
 float ay2;
 float az2;
 float gx2;
 float gy2;
 float gz2;

} __attribute__((__packed__))data_packet_t;

data_packet_t dp;

template <typename T> void sendData(T data)
{
 unsigned long uBufSize = sizeof(data);
 char pBuffer[uBufSize];

 memcpy(pBuffer, &dp, uBufSize);
 Serial.write('^');
 for(int i = 0; i<uBufSize;i++) {
   if(pBuffer[i] == '^')
   {
    Serial.write('^');
    }
   Serial.write(pBuffer[i]);
 }
}
void setup() {
  Serial.begin(57600);
}
void loop(){
dp.ax1 = 0.03; // Note that I didn't fill in the others. Too much work. ;p
sendData<data_packet_t>(dp);
}

Код Python:

import serial
from  copy import copy
from struct import *


ser = serial.Serial(
#   port='/dev/cu.usbmodem1412',
  port='/dev/ttyUSB0',
#     port='/dev/cu.usbserial-AL034MCJ',
    baudrate=57600
)



def get_next_data_block(next_f):
    if not hasattr(get_next_data_block, "data_block"):
        get_next_data_block.data_block = []
    while (1):
        try:
            current_item = next_f()
            if current_item == '^':
                next_item = next_f()
                if next_item == '^':
                    get_next_data_block.data_block.append(next_item)
                else:
                    out = copy(get_next_data_block.data_block)
                    get_next_data_block.data_block = []
                    get_next_data_block.data_block.append(next_item)
                    return out
            else:
                get_next_data_block.data_block.append(current_item)
        except :
            break


for i in range(1000): # just so that the program ends - could be in a while loop
    data_ =  get_next_data_block(ser.read)
    try:
        print unpack('=ffffffffffff', ''.join(data_))
    except:
        continue
ответил Guru Subramani 24 Maypm17 2017, 19:03:57

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132