Отправить структуру через Serial

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

 struct Gyro_data_structure {
    char command_name[5];
    float gyro_X;
    float gyro_Y;
    float gyro_Z;
};

Gyro_data_structure Gyro_data;

int size_gyro=sizeof(struct Gyro_data_structure);

void setup() {
  Serial.begin(9600); // открывает последовательный порт, устанавливает скорость передачи данных 9600 бит/с
}

void loop() {
  Gyro_data.command_name[0]='H';
  Gyro_data.gyro_X++;
  Serial.println(size_gyro);
  delay(500);
  Serial.write(Gyro_data,size_gyro);
}

Однако он вернул сообщение об ошибке "Нет подходящей функции для вызова 'HardwareSerial:write(Gyro_data_structure&,int&)'". Я погуглил и нашел ответ Микаэля Роя здесь . Однако не работает: "не удается преобразовать..."

Как отправить структуру через UART?

, 👍2

Обсуждение

я бы начал с кода, который работает, а затем внес небольшие изменения, чтобы выяснить, что работает, а что нет.... используйте код, который вы нашли в качестве примера... измените свою структуру на все целые числа, @jsotola

@jsotola в настоящее время ни один из них не работал, единственное, что я не пробовал, это union. Прямо сейчас я пробую побитовый оператор, но, видимо, int подпален., @ShoutOutAndCalculate

начните с того же скетча, что и ссылка, которую вы разместили, @jsotola

@jsotola Вот в чем вопрос, это не сработало., @ShoutOutAndCalculate

вы можете отправить структуру как двоичную, только если получатель использует тот же способ хранения двоичных данных в структуре, что и отправитель. в основном это будет работать, только если получатель использует тот же компилятор, @Juraj


2 ответа


Лучший ответ:

5

Я попробовал код по указанной вами ссылке, и он сработал:

struct Gyro_data_structure
{
    char command_name[6];
    int gyro_X;
    int gyro_Y;
    int gyro_Z;
};

struct Gyro_data_structure Gyro_data = {"Hello", 48, 49 , 50};

int size_gyro = sizeof(struct Gyro_data_structure);

void setup() 
{
  Serial.begin(9600); // открывает последовательный порт, устанавливает скорость передачи данных 9600 бит/с
}

void loop()
{
  send(&Gyro_data);
  Serial.println();
  delay(1000);
}

void send (const Gyro_data_structure* table)
{
  Serial.write((const char*)table, size_gyro);  // 2 байта.
}

bool receive(Gyro_data_structure* table)
{
  return (Serial.readBytes((char*)table, sizeof(Gyro_data_structure)) == sizeof(Gyro_data_structure));
}

Я изменил тип float на int для простоты представления. Когда вы запустите код, серийное окно покажет: Hello012

Поскольку в последовательном окне Arduino IDE отображаются символы ASCII. Значение ASCII для 0 равно 48 и т. д.


Обновить

Это зависит от того, что вы хотите от функции чтения. Давайте разберем, что делает реализация Майкла Роя. Сначала он считывает sizeof(Gyro_data_structure) число последовательных байтов и помещает его в переменную, на которую указывает таблица:

Serial.readBytes((char*)table, sizeof(Gyro_data_structure))

Второй аргумент функции readBytes() принимает количество байтов, которое вы хотите прочитать. Также readBytes() возвращает количество прочитанных байтов. Допустим, мы сохранили это число в return_bytes. В следующей части он сделал:

return (return_bytes == sizeof(Gyro_data_structure))

Он проверяет, совпадает ли количество прочитанных readBytes() байтов с размером Gyro_data_structure. Если это не так, верните false.

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

Если вы не хотите, чтобы функция ничего не возвращала, вот как это должно быть:

void receive(Gyro_data_structure* table)
{
  Serial.readBytes((char*)table, sizeof(Gyro_data_structure));
}
,

Спасибо ооооочень большое!!!!! Это было действительно полезно! Однако не могли бы вы немного объяснить, что там произошло? (char*) и отправить(&Gyro_data); получить(&Gyro_data); Я хочу изучить и настроить код для лучшей помехоустойчивости., @ShoutOutAndCalculate

send() принимает указатель Gyro_data_structure. Поэтому в цикле() send() использует адрес объявленной переменной (Gyro_data). Когда вы хотите отправить адрес любой переменной, вы используете «&» перед этой переменной. Теперь адрес указывает на первую переменную структуры, которая имеет имя_команды[0]. Serial.write() начинает печатать по одному байту за раз из этой переменной, пока не достигнет size_gyro., @Fahad

Не могли бы вы немного объяснить, почему принимающая часть использовала bool вместо void? и разве второй ввод Serial.readBytes не равен 1? (скажем, 12==12 ), @ShoutOutAndCalculate

@ShoutOutAndCalculate Я обновил свой ответ, чтобы отразить ваш комментарий., @Fahad


2

Во-первых, вам нужно решить, хотите ли вы отправлять данные как необработанные байты («двоичные данные») или в виде текстового представления ASCII. Бинарный имеет тенденцию быть более эффективным: вы можете отправить float с полной точностью всего в четыре байта, тогда как обычно требуется от 8 до 9 значащих цифры для восстановления полной точности из представления ASCII. Однако с двоичным кодом сложнее работать: некоторые типы имеют разные двоичные значения. представления на вашем Arduino и вашем ПК, и вам может потребоваться определить своего рода протокол, чтобы получить правильное оформление. ASCII проще, и обычно вы можете использовать конец строки как простое устройство кадрирования. я обычно рекомендуется использовать ASCII, если вам действительно не нужны дополнительные эффективность бинарного протокола.

Отправка в виде двоичного файла

Вы можете легко отправлять произвольные двоичные данные, используя метод write(const uint8_t *buffer, size_t size) из объекта Serial:

Gyro_data_structure Gyro_data = {"Gyro", 0.0, 4.0, 5.0};

void loop() {
    Gyro_data.gyro_X++;
    Serial.write((uint8_t *) &Gyro_data, sizeof Gyro_data);
    delay(200);
}

Вот шестнадцатеричный дамп вывода:

0000  47 79 72 6f 00 00 00 80  3f 00 00 80 40 00 00 a0  |Gyro....?...@...|
0010  40 47 79 72 6f 00 00 00  00 40 00 00 80 40 00 00  |@Gyro....@...@..|
[...]

который можно разобрать следующим образом:

47 79 72 6f 00 = {'G', 'y', 'r', 'o', '\0'}
00 00 80 3f    = 0x3f800000 = 1.0f
00 00 80 40    = 0x40800000 = 4.0f
00 00 a0 40    = 0x40a00000 = 5.0f

Обратите внимание, что числа с плавающей запятой отправляются с прямым порядком байтов: чтобы байт первый. Приведенные выше 32-битные шестнадцатеричные числа являются двоичными представлениями float.

Отправка как текст

Serial.print() предназначен для печати текстового представления данных. вы даете это. Но так как он не умеет представлять Gyro_data_structure, вы должны Serial.print() каждый член данных в поверните, а затем добавьте форматирование вокруг всего этого:

void print_gyro(const Gyro_data_structure &gyro) {
    Serial.print(F("Gyro{command = \""));
    Serial.print(gyro.command_name);
    Serial.print(F("\", X = "));
    Serial.print(gyro.gyro_X);
    Serial.print(F(", Y = "));
    Serial.print(gyro.gyro_Y);
    Serial.print(F(", Z = "));
    Serial.print(gyro.gyro_Z);
    Serial.print(F("}"));
}

void loop() {
    Gyro_data.gyro_X++;
    print_gyro(Gyro_data);
    Serial.println();
    delay(200);
}

Вывод

Gyro{command = "Gyro", X = 1.00, Y = 4.00, Z = 5.00}
Gyro{command = "Gyro", X = 2.00, Y = 4.00, Z = 5.00}
[...]

Подготовка данных для печати

Это разновидность предыдущей техники. Вы можете Serial.print() ваш объект напрямую, если вы сообщите ядру Arduino, как напечатать это тип объектов. Это делается путем предоставления вашему классу наследования от Printable и реализации виртуального метода printTo(). Но потом класс больше не является «агрегатом» и, следовательно, нуждается в явном конструктор:

struct Gyro_data_structure : Printable
{
    char command_name[5];
    float gyro_X;
    float gyro_Y;
    float gyro_Z;
    Gyro_data_structure(const char *name, float x, float y, float z)
    : gyro_X(x), gyro_Y(y), gyro_Z(z) {
        strncpy(command_name, name, 4);
        command_name[4] = '\0';
    }
    size_t printTo(Print& p) const {
        size_t count = 0;
        count += p.print(F("Gyro{command = \""));
        count += p.print(command_name);
        count += p.print(F("\", X = "));
        count += p.print(gyro_X);
        count += p.print(F(", Y = "));
        count += p.print(gyro_Y);
        count += p.print(F(", Z = "));
        count += p.print(gyro_Z);
        count += p.print(F("}"));
        return count;
    }
};

Gyro_data_structure Gyro_data("Gyro", 0.0, 5.0, 5.0);

void loop() {
    Gyro_data.gyro_X++;
    Serial.println(Gyro_data);
    delay(200);
}

Вывод идентичен предыдущему. Определение класса получает немного корявее, но зато можно очень легко печатать на любой сериал порт (будь то UART или программная реализация), на LCD... или все, что понимает print().

,