Передача структуры между NANO и ESP8266

UserUserovotch

✩✩✩✩✩✩✩
24 Ноя 2020
12
1
Как передать структуру между Ардуино и ESP8266 по UART?
Перерыл все форумы, нигде нет ответа.
Задача такая: есть датчик температуры, подключенный к NANO, NANO по Lora (подключен к UART) обменивается данными с ESP8266 (к ESP подключена Lora тоже по UART).
Для начала работы датчика отправляется запрос от ESP8266 к NANO с настройками. Настройки передаю через структуру. В структуре данные разных типов - String, float, uint16_t, uint8_t.
При передаче от ESP к NANO эта структура принимается, но происходит сдвиг между битами и в принятой структуре данные получаются не правильными.
Про порядок расположения данных читал - сначала "тяжелые", потом "легкие".
Нашел что-то близкое на форумах, там пишут про передачу данных побитно.
Передатчик:
C++:
struct StrSend {
  String testStr;
  uint16_t test;
  uint8_t  crc;
};
StrSend dataTx;

void setup () {
  SendStr.testStr = "testing";
  SendStr.test = 1234;
  SendStr.crc = crc8((byte*)&dataTx, sizeof(dataTx) - 1);
}

void loop() {
Tx();
delay(1000);
}

void Tx() {
    for (byte i = 0; i < sizeof(dataTx); i++) {
          LoraUART.print(*((byte*)&dataTx + i));
    }
}

byte  crc8(byte *buffer, byte size){
    byte crc = 0;
    for(byte i = 0; i < size; i++){
        byte data = buffer[i];
        for(int j = 8; j > 0; j--){
           crc = ((crc ^ data) & 1) ? (crc >> 1) ^ 0x8C : (crc >> 1);
           data >>= 1;
        }
    }
    return crc;
}
Как правильно принять эти данные?

И второй вопрос:
Не правильно считается crc на ESP. Пробовал посчитать без отправки структуры:

C++:
void setup () {
  SendStr.testStr = "testing";
  SendStr.crc = crc8((byte*)&SendStr, sizeof(SendStr) - 1);

  byte crc = crc8((byte*)&SendStr, sizeof(SendStr));
  Serial.println(crc);
}
Получается случайное число. На NANO crc считается верно.
 
Изменено:

rkit

★★★✩✩✩✩
5 Фев 2021
510
127
Нужно детерминистично привести структуру в поток байтов, потом из этого потока байтов сформировать структуру. Ты просто посылаешь блок памяти, занимаемый данными, без учета того, что разные процессоры эту память интерпретируют по-разному. Не говоря уж о том, что данные у тебя занимают несколько дискретных блоков памяти.
 
  • Лойс +1
Реакции: UserUserovotch

Геннадий П

★★★★★★✩
14 Апр 2021
1,975
634
45
Нужно детерминистично привести структуру в поток байтов, потом из этого потока байтов сформировать структуру.
Это называется сериализация и соответственно десериализация.
Сериализация может быть в любом удобном для передачи данных формате: хоть поток байтов, хоть в текстовом виде.
 
  • Лойс +1
Реакции: UserUserovotch

UserUserovotch

✩✩✩✩✩✩✩
24 Ноя 2020
12
1
А почему неверно считает crc? Точнее верно для nano, но ошибочно для esp?
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,975
634
45

UserUserovotch

✩✩✩✩✩✩✩
24 Ноя 2020
12
1
@Геннадий П,
Так я использую одну и туже функцию в обоих случаях. Почему в одном месте она считает правильно(нано), а во втором - нет (есп). Даже если не передавать между контроллерами данные тут же проверить - то выдает вместо 0 значение, что говорит об ошибке контрольной суммы.
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@UserUserovotch, дело в том, что есть такая штука, как выравнивание и длина слова. Atmega 328 - 8-битный процессор, все байты у него идут подряд. Семейство ESP - 32-разрядные, потому два стоящих типа int8_t будут разделены (выравнены) на границу слова.
Ради интереса можете вывести sizeof Вашей структуры на Nano и потом - на ESP.
 

UserUserovotch

✩✩✩✩✩✩✩
24 Ноя 2020
12
1
@Геннадий П,
Я отсюда брал. Тут не правильно, получается написано?

@poty,
Да, уже видел про pragma pack. Но есть мнение, что это костыли, которые могут привести в неожиданным последствиям. Или есть другие варианты?
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,975
634
45
@UserUserovotch, Не знаю, лень читать.
Смысл в том, что ЦРЦ считается структуры в целом.
Изначально ЦРЦ = 0, посчитается определенное значение и перезапишет значение ЦРЦ в структуре. И соответственно ЦРЦ структуры изменится и при проверке будет не верным.
Выход: при приеме запомнить ЦРЦ из принятой структуры, обнулить значение ЦРЦ в структуре, и только потом проверять ЦРЦ структуры с тем значением что запомнили ранее.
Либо, как написал выше - не использовать запоминание ЦРЦ в структуре.
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@UserUserovotch, вариантов немного: передавать отдельные значения, а не структуру целиком и проверять их сначала в битовом виде, а потом уже приводить к типу, принятому на приёмной стороне. Если честно, для термометра передавать структуру... для управления... это зачем?
 

UserUserovotch

✩✩✩✩✩✩✩
24 Ноя 2020
12
1
@poty,
Чтобы подключить несколько датчиков и присваивать каждому уникальный id (текстовая строка), выставлять время сна (Лонг), задавать начальные(калибровочные) значения (флоат) и настройку канала и адреса Лоры (по биту на каждый параметр - три бита) и контрольная сумма (биты).
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
ID может хранить Нано, достаточно передать лишь номер (думаю, их не будет больше 256), связанный с хранящимся в нано ID.
Для чего сон датчику? Не опрашивайте его - он уйдёт в сон.
Калибровочные данные? Термометру? Вы ничего не напутали?
Канал и адрес Лоры - совершенно отдельная процедура, незачем её мешать с опросом датчика.
Контрольная сумма может применяться к каждой команде. Достаточно только упаковать это во что-то стандартное. Например, в unsigned long, а потом из него распарсить.
 

UserUserovotch

✩✩✩✩✩✩✩
24 Ноя 2020
12
1
@poty,
Конечно, не каждый опрос датчика требует всех данных. Но для инициализации эти все данные нужно передать в датчик. Для формирования этих параметров написан простенький сайт и создана база данных. Если нано ушла в сон - ее же надо разбудить. Я хотел делать это по таймеру, чтобы иметь возможность выключить и нано и Лору (питание батарейное). А обратно датчик должен передавать свой id, показания температуры, давления, влажности (bme280) и заряд батарей (вольтметр через аналоговые входы). Для обмена всем этим зоопарком я и хотел структуру использовать. Лора даёт возможность гонять до 512 байт, так что места хватит с запасом, просто заполнять не всё поля, а только нужные. Так могу сделать универсальную структуру, принимая ее всегда одну и ту же, а использовать в зависимости от флага (который тоже в структуре) только нужные поля. Такая идея. На нанах я ее реализовал, сейчас добавляю элементы автоматизации и пользовательского интерфейса, для чего удобнее использовать есп как шлюз между экраном с кнопками (комп, телефон, что угодно) и датчиками на Ардуино. Уткнулся в вопрос передачи между есп и Ардуино. Можно через массив символов все сдать, но хотелось через структуру, так как уже есть наработки.
 

rkit

★★★✩✩✩✩
5 Фев 2021
510
127
Избыточное цитирование. Отредактируй или сообщение будет удалено
C++:
void setup () {
SendStr.testStr = "testing";
SendStr.crc = crc8((byte*)&dataTx, sizeof(dataTx) - 1);

byte crc = crc8((byte*)&dataTx, sizeof(dataTx));
Serial.println(crc);
}
Получается случайное число. На NANO crc считается верно.
Ну, судя по этому огрызку, у тебя dataTx не инициализирована.
 

rkit

★★★✩✩✩✩
5 Фев 2021
510
127
C++:
void setup () {
SendStr.testStr = "testing";
SendStr.crc = crc8((byte*)&SendStr, sizeof(SendStr) - 1);

byte crc = crc8((byte*)&SendStr, sizeof(SendStr));
Serial.println(crc);
}
А теперь совершенно правильно получается случайное число, потому что string хранит данные вне структуры, и адрес данных - случайный.
 
  • Лойс +1
Реакции: Lumenjer

UserUserovotch

✩✩✩✩✩✩✩
24 Ноя 2020
12
1
@rkit,
Как вне структуры? Сруктура определена в глобальных переменных. Ей присвоено значение. Почему она вне структуры?
 

rkit

★★★✩✩✩✩
5 Фев 2021
510
127
Потому что так работает это класс. Открой исходник и посмотри.
 

rkit

★★★✩✩✩✩
5 Фев 2021
510
127
Где "там" я не знаю. В твоем коде ссылок нет. В чем проблема я уже объяснил и повторяться не буду.
 

UserUserovotch

✩✩✩✩✩✩✩
24 Ноя 2020
12
1
На самом деле там проблема с выравниванием данных при передаче между 8 и 32 битными архитектурами.
"Там" - это в коде.
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915

@UserUserovotch,
слушайте ркит, он дело говорит. Пока вы не выкинете String из структуры, у вас ничего работать не будет.
И даже "костыли" как вы говорите типа #pragma pack - вам не помогут, потому что размер данных всегда будет разный.

@rkit,
Так там же ссылка на адрес передается.
вот именно. Потому и не работает.Вместо самой строки передается ссылка на нее. Например при передаче с Есп на нано передается адрес строки в памяти есп. И что, по вашему, мк Нано должен делать с этой ссылкой?
 

UserUserovotch

✩✩✩✩✩✩✩
24 Ноя 2020
12
1
@bort707,
Не совсем так. Ссылка передается при расчете crc. Передача идёт байт структуры. Но из-за разной архитектуры есп и нано данные занимают разное количество байт. Если сделать выравнивание, то все заработает нормально.
Уже нашел решение, не могу найти как тут отметить, что проблема решена.
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915

@UserUserovotch, ошибаетесь.
Ваша структура data.tx байтов строки не содержит и потому при передаче структуры они не передаются.
Если вы "нашли решение" - покажете код, который передает вашу структуру без ошибок.