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

poty

★★★★★★✩
19 Фев 2020
3,261
948
@UserUserovotch, есть две проблемы, которые Вы обозначили ранее:
1. Неправильно передаются данные. Данные должны быть сосредоточены непосредственно в области памяти, которую Вы передаёте. Если в этой области памяти располагаются указатели, то они и передаются, данные, расположенные по указателю, не передаются.
2. Неправильно рассчитывается CRC. Это, я считаю, происходит из-за выравнивания в памяти.
 
  • Лойс +1
Реакции: bort707

Lumenjer

★★★✩✩✩✩
10 Дек 2020
220
112
@UserUserovotch, Попробуйте вывести sizeof(SendStr), потом запихнуть
SendStr.testStr = "testingtestingtestingtestingtesting";
И повторно вывести sizeof(SendStr), вы тогда поймете о чем говорит @rkit

Вам нужно не строку в структуре хранить, а фиксированный char массив нужного размера, куда будете помещать инфу и уже так передавать.

**
Я отсюда брал. Тут не правильно, получается написано?
Нормально там, последний байт в счет не берется, т.к. sizeof(data) - 1
 
Изменено:

Maxim_01

✩✩✩✩✩✩✩
17 Май 2021
54
7
Нижний Новгород
Всем добрый день!
У меня тоже проблема с расчетом CRC на ESP-01. Он получается не такой как на Нано, хотя данные прилетают правильные.
у Гайвера написано: "Компилятор esp очень странно пакует байты в структуру, поэтому однобайтные типы данных нужно размещать в конце структуры. Например так:
struct myStruct {
float val_f;
float val_f2;
int val_i;
long val_l;
byte val_b;
};

Не понимаю как мне правильно разместить переменные в моей структуре, т.к я передаю еще два массива.
Может есть у кого мысли по этому поводу?
struct Str {
float val_f;
float val_f2;
bool val_i[9];
byte val_l[3];
byte val_b;
byte crc;
};
 
Изменено:

poty

★★★★★★✩
19 Фев 2020
3,261
948
@Maxim_01, "разрядность" микроконтроллеров, установленных в Nano и ESP, разная, поэтому выравнивание внутри сложных типов данных, типа структуры, тоже разное. Фактически, в ESP появляются байты-заполнители, которые участвуют при обращении к структуре как к памяти. Они и приводят к неверному расчёту CRC.
 

Maxim_01

✩✩✩✩✩✩✩
17 Май 2021
54
7
Нижний Новгород
Исходя из вышесказанного какие можете дать рекомендации по расчету Crc на esp?
Или рассчитать CRC на esp и Arduino с получением одинакового значения невозможно?
(Что тогда имел ввиду Гайвер в своей статье)
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@Maxim_01, почему невозможно? Запишите принятые данные в массив байт, посчитайте в нём CRC, а потом раскидывайте этот массив по полям структуры.
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915
рассчитать CRC на esp и Arduino с получением одинакового значения невозможно?
возможно. При описании структур на обоих контроллерах используйте pragma pack(1) и crc будет одинаково считаться на обоих мк.
Что имел ввиду Гайвер в своей статье
Алекс очень креативный человек, но и в его гайдах попадаются ошибки
 
Изменено:
  • Лойс +1
Реакции: Maxim_01

rkit

★★★✩✩✩✩
5 Фев 2021
510
127
Есть такие типы, которые могут быть чем попало на разных контроллерах и даже с разными настройками компиляторов на одном контроллере.
 

Maxim_01

✩✩✩✩✩✩✩
17 Май 2021
54
7
Нижний Новгород
Ребята, спасибо всем, что подключились!
Конструкция с #pragma pack спасла ситуацию. Все заработало как нужно.
В интернете пишут, что это не очень хорошее решение, но я не понял почему(
#pragma pack (push, 1)
struct StrWIFI {
float Temper;
byte Vlazhn;
byte D[4];
bool R[9];
byte crc;
} ;
#pragma pack (pop)
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,412
978
58
Марий-Эл
@Maxim_01, Всё зависит от разрядности МК. Если МК 8ми разрядный, то 8ми битная переменная займёт байт.
16 битная, займёт два байта. С boolean сложнее, всё зависит от компилятора. Он может несколько boolean переменных упаковать в один байт, а может для каждой переменной выделить по байту.
В 32 разрядных МК, 8 бит переменная займёт 32 бита. 16 битная займёт те же 32 бита.
Директива pack заставляет компилятор упаковать это поплотнее. Т.Е. на 8 бит отвести 8 бит, на 16 бит отвести 16. И размещаться они будут в одной ячейке памяти.
Что это даёт?
Если данные не пакованы, то они занимают в памяти больше места, но доступ к ним более быстрый, что бы считать переменную достаточно считать ячейку.
Если данные пакованы, они занимают меньше места, но приходится их выделять. Т.Е. считать ячейку в которой находится несколько переменных, выделить нужную. При необходимости использовать сдвиг, что замедляет работу с переменной.
Так что использовать пакованные или непакованные данные отдаётся на усмотрение программисту.
В Вашем случае немного непонятно, почему работает в одном случае, но не работает в другом.
Просто, если данные не пакованы, передаётся большее количество байт и всё.
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915
В 32 разрядных МК, 8 бит переменная займёт 32 бита. 16 битная займёт те же 32 бита
Эдик, вы ошибаетесь.
Из того, что память МК организована 32битными словами, вовсе не следует, что каждая переменная занимает целое слово. Современные компиляторы достаточно умны, чтобы разместить в одном слове, например, 4 переменных типа uint8 или две uint16
Причина проблем не в этом, почитайте про alignment.
По умолчанию переменные типа uint32 выравниваются в Есп32 по границе слова. Поэтому если создаем структуру из одного лонга и одного байта, например

struct s1 {
long l;
byte b;
}

или

struct s2 {
byte b;
long l;
}
то размер структуры в памяти будет зависеть от порядка элементов, структура s1 займет 5 байт, а s2 - 8байт.
В случае же 8битного мк переменные выравниваются по границе байта и размер s1 и s2 будет одинаков.
В итоге при передаче по сети структуры s2 размер ее на разных концах линии не совпадает и crc не сходится.
А чем нам помогает директива pack - догадайтесь сами
 
Изменено:

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,412
978
58
Марий-Эл
По умолчанию переменные типа uint32 выравниваются в Есп32 по границе слова
Я говорю об общем принципе, а не конкретном МК.
С ESP я никогда не работал и работать не буду. Принципиально.
То, что у него сделано так, не означает, что так везде.
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915
То, что в Есп32 сделано так, не означает, что так везде.
Хорошо, если это " общий принцип" - назовите МК где сделано так, как пишете Вы, чтобы переменная размера 8бит занимала все 32битное слово.
У стм32 тоже не так
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,412
978
58
Марий-Эл
@Maxim_01, Концепция не нравится. Атмел мне тоже не зашёл.
Я раньше на АСМе писал. У атмела ассемблер тупой до жути. И как то так сложилось исторически.

У стм32 тоже не так
У STM именно так.
Сам пример найти не могу. Валялся где то. Там очень хорошо показано как это происходит.
Ладно бы с флешем, где лежит прога. С EEPROM 146% байт занимает всё машинное слово в 32 бита.
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,412
978
58
Марий-Эл
@bort707, Одно пари я проиграл.
Но на работе буду, проверю.
Заодно постараюсь найти ту статью, которую я читал.
Здесь ещё может быть нюанс. В оциях компилятора может быть указано, что данные пакованы.
Нужно это то же проверить.
Но мой друган напарывался уже на это и не мог понять, почему данных уходитт больше, чем должно быть.
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915
Здесь ещё может быть нюанс. В оциях компилятора может быть указано, что данные пакованы.
Нужно это то же проверить.
это тоже несложно проверить....
Вот прям сейчас и увидим. Я вернулся с дачи, жены дома нет, дочь гуляет...
Код:
C++:
struct  {
uint8_t b1;
uint8_t b2;
uint8_t b3;
uint8_t b4;
} s1;

struct  {
uint8_t b;
uint32_t l;
} s2;

struct  {
uint32_t l;
uint8_t b;
} s3;

void setup() {
  Serial.begin(115200);
  delay(5000);
  Serial.print("Size of s1: ");
  Serial.println(sizeof(s1));
  Serial.print("Size of s2: ");
  Serial.println(sizeof(s2));
  Serial.print("Size of s3: ");
  Serial.println(sizeof(s3));
}
void loop() {}
Результат:
C++:
Size of s1: 4
Size of s2: 8
Size of s3: 8
Как видно, структура s1 из 4 байт занимает именно 4 байта, а вовсе не 16. Причем то, что структуры s2 и s3 занимают по 8 байт - показывает, что никаких опций упаковки при компиляции не использовалось.

Правда, вынужден признать, что я тоже кое в чем ошибся - я полагал что структуры s2 и s3 будут иметь разный размер - а они оказались одинаковы. Однако это вовсе не потому, что поле uint8_t b занимает целое слово. Похоже что компилятор выравнивает не олько начало структуры по границе слова, но и конец.

Это, опять же, нетрудно проверить, добавив в код структуру s4
C++:
struct  {
uint32_t l;
uint8_t b1;
uint8_t b2;
} s4;
Ее размер оказывается те же 8 байт, что и s2 s3 - что доказывает, что это именно выравнивание, а не размещение одного байта в целом слове.
(проверялось на STM32F103C8 bluepill в среде Ардуино 1.8.3)

Ну и для полноты картины - размеры этих структур на Ардуино Нано(код тот же)
C++:
Size of s1: 4
Size of s2: 5
Size of s3: 5
Size of s4: 6
Надеюсь, что вопрос передачи структур с Нано на 32битные МК теперь станет яснее.
 
Изменено:

poty

★★★★★★✩
19 Фев 2020
3,261
948
@bort707, теперь бы ещё решить вопрос выравнивания при использовании слов как операндов.
И, кстати, если uint8_t переменные объявить вне структуры - они будут выравниваться?
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915
ещё решить вопрос выравнивания при использовании слов как операндов.
не очень понимаю, какое именно "слово" вы имеете в виду. Выше я "словами" называл ячейки памяти по 4 байта, но как их использовать как операнды - я не понимаю
если uint8_t переменные объявить вне структуры - они будут выравниваться?
считаю что нет, но проверить это сложнее...
 
Изменено:

poty

★★★★★★✩
19 Фев 2020
3,261
948
"слово" = word по-английски. Один из размеров операнда в ассемблере.
А почему сложнее проверить? Можно вывести адреса и по ним определить выравнивание.
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,412
978
58
Марий-Эл
Это три структуры

1657434048021.png

Это размер первой структуры
1657434094893.png

Это размер второй структуры.
1657434146192.png

Это размер 3й.
1657434190496.png

pack не сработало. Инета нет узнать как правильно написать.

Компилятор упаковал b1,b3 в одну 32 разряда ячейку.
B5 во вторую 32 разрядную
b4, b2 в третью.
Результаты довольно странные.
Ни вашим, ни нашим.

Нужно саму память потом порыть. Как там всё лежит.
 

Вложения