Передача struct по I2C

Есть мастер ESP8266:

#include <Wire.h>
#include <CRC8.h>
#include <CRC.h>

#define SLAVE_ADDR 8      

struct __attribute__((__packed__)) COUNTER {
  uint32_t ONLINE;
  uint32_t RESULT;
};

struct __attribute__((__packed__)) SEND {
  uint8_t START = 255;
  COUNTER CBM20;
  COUNTER CBT11A;
  COUNTER CI3BG;
  uint8_t CRC;
};

SEND SEND;

void setup() {
  Wire.begin(D1, D2);                            
}

void loop() {
  SEND.CBM20.ONLINE = 3436546546;
  SEND.CBM20.RESULT = 3455345345;

  SEND.CBT11A.ONLINE = 246556754;
  SEND.CBT11A.RESULT = 345674645;

  SEND.CI3BG.ONLINE = 98574;
  SEND.CI3BG.RESULT = 7604646;

  SEND.CRC = calcCRC8((uint8_t*)&SEND, sizeof(SEND) - 1);
  Wire.beginTransmission(SLAVE_ADDR);
  Wire.write((uint8_t*)&SEND, sizeof(SEND));
  Wire.endTransmission();
}

Есть слушатель Arduino NANO:

#include <Wire.h>
#include <CRC8.h>
#include <CRC.h>

struct __attribute__((__packed__)) COUNTER {
  uint32_t ONLINE;
  uint32_t RESULT;
};

struct __attribute__((__packed__)) SEND {
  uint8_t START = 255;
  COUNTER CBM20;
  COUNTER CBT11A;
  COUNTER CI3BG;
  uint8_t CRC;
};

SEND SEND;


void setup() {
 Wire.begin(8);
 Wire.onReceive(receiveEvent);
 Serial.begin(115200);
}

void receiveEvent() {
  Wire.readBytes((uint8_t*)&SEND, sizeof(SEND));
}

void loop() {
  Serial.print(SEND.CBM20.ONLINE);
  Serial.print(" = ");
  Serial.print(SEND.CBM20.RESULT);
  Serial.print(" : ");
  Serial.print(SEND.CBT11A.ONLINE);
  Serial.print(" = ");
  Serial.print(SEND.CBT11A.RESULT);
  Serial.print(" : ");
  Serial.print(SEND.CI3BG.ONLINE);
  Serial.print(" = ");
  Serial.println(SEND.CI3BG.RESULT);
}

Всё работает, данные передаются:

0 = 0 : 0 = 0 : 0 = 0
0 = 0 : 0 = 0 : 0 = 0
0 = 0 : 0 = 0 : 0 = 0
0 = 0 : 0 = 0 : 0 = 0
0 = 0 : 0 = 0 : 0 = 0
0 = 0 : 246556754 = 345674645 : 98574 = 7604646
3436546546 = 3455345345 : 246556754 = 345674645 : 98574 = 7604646
3436546546 = 3455345345 : 246556754 = 345674645 : 98574 = 7604646
3436546546 = 3455345345 : 246556754 = 345674645 : 98574 = 7604646
3436546546 = 3455345345 : 246556754 = 345674645 : 98574 = 7604646
3436546546 = 3455345345 : 246556754 = 345674645 : 98574 = 7604646
3436546546 = 3455345345 : 246556754 = 345674645 : 98574 = 7604646

Нули я могу отсеять проверкой CRC, но правильно ли так передавать данные?

Может такая конструкция сбиться, не нужно ли алгоритм передачи данных перестроить по другому?

1 лайк

А разве crc SEND не изменится после таких манипуляций?
Или получается, что если в структуре uint8_t crc в конце объявлен, то и в области памяти он будет последним (всегда)?

Да, с помощью __attribute__((__packed__))последним байтом будет uint8_t CRC; так как он в структуре объявлен последним.

А на слушателе можно удобно проверять структуру:

if (calcCRC8((uint8_t*)&DATA, sizeof(DATA)) == 0)

CRC в этом случае всегда будет равна 0

Я бы вместо

написал бы

SEND.CRC = calcCRC8((uint8_t*)&SEND, sizeof(SEND) - sizeof(SEND.CRC));

но, это дело хозяйское.

1 лайк

Понял, спасибо.

1 лайк

Обычно, когда контрольные суммы в пакетах данных присутствуют, делают так:

  1. зануляют поле КС в структуре.
  2. считают КС
  3. вписывают назад

при таком подходе вовсе не обязательно пихать CRC в конец структуры и вычитать ее длину потом. Считайте вместе с этим полем, просто занулите его перед использованием :slight_smile:

А еще лучше , вообще отказаться от контрольной суммы и передавть просто два байта - один в начале , другой в конце. 0x55 в начале, 0xaa в конце. Если у вас линия шумная, то там испортится весь пакет и, в частности, байты-маркеры. Так проще и сумму считать не нужно.

А если нет? Ну вот “испортится” всего лишь один байтик в середине посылки.
Который отвечает за запуск “Орешника”, например.. Ой-ёёёё.. :wink:
Иногда, сами знаете, лучше перебдеть и переспросить не принятое..

1 лайк

ну, если орешника. не нужно пытаться заранее предугадать все проблемы.

из моего опыта - если шум, то портится чуть ли не половина данных, а то и вовсе - все.

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

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

1 лайк

Использовать кодирование с исправлением ошибок.

Сэр в курсе, что шум может быть разной интенсивности?
Или, если меньше половины питания, то и море по колено не шум вовсе?

1 лайк

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

Но в качестве саморазвития, почему бы и не прикрутить CRC, в принципе. Но если вы за отказоустойчивость радеете, тогда опять же, ЦРЦ

особо не поможет. Пришел вам пакет, выяснилось что битый. Ну так чо, запускать Орешник? Или нет? Или теперь сообщать как-то, “перепошли еще разок, пакетик испортился“?

Нет, это все неправильный подход. Вот вам, от вояк: Шлите по два (три , десять) одинаковых пакета. Закрытые контрольной суммой, если уж прям орешник-орешник