Как преобразовать структуру в char[] для использования в Wire.h или есть лучший способ?

Мне нужно соединить микроконтроллер ATTiny88, настроенный как ведомое устройство I²C, с Raspberry Pi, настроенным как ведущее устройство I²C. Возможно, я использую неправильный подход, но у меня есть структура, состоящая из четырёх различных типов данных фиксированной длины, которые я хочу передавать ведущему устройству по каждому запросу.

Исходный код:

#include <Wire.h>
struct Response // 7 байт
  {
      byte Command;         // 1 байт
      float Temperature;    // 4 байта
      byte FanValue;        // 1 байт
      bool CoolStat;        // 1 байт
  };

struct Response Reply;
bool True = 1;
bool False = 0

void setup()
{
    pinMode(ADDR1, INPUT_PULLUP);   // Смещение адреса 0 или 1
    pinMode(ADDR2, INPUT_PULLUP);   // Смещение адреса 0 или 2
    I2C = (2 * digitalRead(ADDR1)) + digitalRead(ADDR1) + 8;    // Установить адрес I2C 8 - 11
    Wire.begin(I2C);
    Wire.onRequest(requestEvent);   // Вызвать requestEvent при запросе данных

    Reply.Command = 1;
    Reply.Temperature = 35.62;
    Reply.FanValue = 255
    Reply.CoolStat = True
}

// Функция, которая выполняется всякий раз, когда данные запрашиваются из главного устройства
void requestEvent() {
    Wire.write(Reply);         // ответить сообщением, как ожидает мастер
    Responded = True;
}

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

, 👍1


1 ответ


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

3

Существует перегрузка Wire.write, которая позволяет отправлять произвольные данные массив байтов:

virtual size_t write(const uint8_t *, size_t);

Чтобы использовать его, вам нужно преобразовать адрес вашей структуры в указатель на uint8_t:

Wire.write((uint8_t *) &Reply, sizeof Reply);

Однако следует отметить, что этот метод, хотя и очень распространен, на самом деле не является совместимо со стандартом C++. Я предлагаю вам вместо этого использовать union, который позволяет получить доступ к памяти структуры как к простому массиву байтов:

union Response {
    struct {
        byte Command;         // 1 байт
        float Temperature;    // 4 байта
        byte FanValue;        // 1 байт
        bool CoolStat;        // 1 байт
    };
    uint8_t bytes[7];
};

Затем это можно отправить с помощью:

Wire.write(Reply.bytes, sizeof Reply);

Однако: у вас могут возникнуть трудности с разбором структуры на Число Пи. Разные вычислительные платформы имеют разные размеры типов данных и Различные ограничения выравнивания. Используемая вами структура может быть Длина 7 байт на Arduino Uno, но на базе ARM он будет больше Arduino, а также на Raspberry Pi: для выравнивания вам нужно будет Перед Temperature есть три дополнительных байта. Также CoolStat Поле может быть больше одного байта и может содержать некоторое дополнительное поле. Чтобы упростить задачу, я рекомендую вам определить структуру таким образом, чтобы его макет не зависит от платформы: используйте только типы фиксированного размера, поместите Сначала более крупные элементы, а затем вручную дополнить до кратного размера более крупного элемента размер:

union Response {
    struct {
        float Temperature;  // 4 байта
        uint8_t Command;    // 1 байт
        uint8_t FanValue;   // 1 байт
        uint8_t CoolStat;   // 1 байт
        uint8_t padding;    // 1 байт
    };
    uint8_t bytes[8];
};

Это должно иметь точно такую же компоновку на любом Arduino, а также на Raspberry Pi.

,

Чтобы избежать выравнивания, можно использовать #pragma pack(1), @SBF

@SBF: Это, вероятно, работает на x86\_64. Однако я предпочитаю этого избегать, так как у меня были компьютеры, на которых невыровненный доступ к памяти приводил к сбою программы с ошибкой SIGBUS (ошибка шины)., @Edgar Bonet

Спасибо, сэр! Ваш ответ идеален., @LesRhorer