Как сохранить структуру в файле?

esp8266 spiffs littlefs

Я пытаюсь сохранить (прочитать и записать() структуру, используя LittleFS на ESP8266.

Благодарим за помощь

struct oper_string
{
  bool state;     /* On or Off */
  uint8_t step;   /* Step, in case of PWM */
  uint8_t reason; /* What triggered the button */
  time_t ontime;  /* Start Clk */
  time_t offtime; /* Off Clk */
};

, 👍0


3 ответа


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

4

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

struct foo{
   bool first;
   uint8_t Second;
   uint16_t Third;
} structMember;

Затем он сохраняется в строке в том же порядке, в котором они были определены

---------------------------
| bool | uint8_t | uint16_t|
|1 byte| 1 bytes | 2 bytes |  
--------------------------
1B+1B+2B = sizeof(srtuctMember)

Файл.write() сохраняет дату в файле блоками по 1 байт каждый.

Когда вы отправляете массив в функцию, она фактически получает только указатель на свое начало. Вот почему многие функции принимают размер в качестве второго аргумента.

Когда вы отправляете данные в функцию в формате, для которого она не предназначена, вы можете указать компилятору, что функция должна определить их как переменную другого типа. Допустим, у нас есть функция, определенная как void sampleFunction (byte var1) то мы можем использовать его

char x='j';
sampleFunction((byte) x);

который указывает компилятору обрабатывать 'x' как если бы он был byte.

когда вы передаете массив, вы на самом деле передаете не фактические данные, а информацию о том, где находятся данные (то же самое с членом структуры), поэтому, ставя звездочку после определения, вы сообщаете компилятору формат данных указывает на. и когда вы прочитаете его обратно, вы получите все спорные данные как те же данные в том же порядке... какая структура затем делится таким же образом

file.write((byte*) &StructMember,sizeof(structMember));
,

Я только хочу, чтобы кто-нибудь объяснил мне это, когда я изо всех сил пытался понять, как это связано с char и utf8 несколько лет назад..., @Tomas

Итак, идея в том, что каждый элемент в этой структуре упоминается как указатель byte/utin8_t? и результатом является массив указателей (без каких-либо разделителей, пробелов и т. д.)?, @Guy . D

@guyd: StructMember — это экземпляр, а не член структуры. Идея состоит в том, что структура в целом должна обрабатываться как массив байтов. Точнее, _address_ структуры следует рассматривать как начальный адрес массива байтов. Это указатель на массив (байтов), а не на массив указателей., @Edgar Bonet

@EdgarBonet - извините за мой английский, но членом структуры является, например, offtime, на который ссылается адрес (&). Спасибо, что прояснили это., @Guy . D


3

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

Структура, приведенная Томасом в качестве примера, не будет иметь заполнения, но ваша май. Например, на моем 64-битном ПК ваша структура хранится следующим образом:

смещение размер участник
0 1 состояние
1 1 шаг
2 1 причина
3 5 (заполнение)
8 8 вовремя
16 8 нерабочее время

В приведенной выше таблице "смещение" – это положение элемента относительно начало структуры. Заполнение после reason необходимо для того, чтобы чтобы выровнять ontime по адресу, кратному 8. Ваш ESP будучи 32-битной машиной, она, скорее всего, будет использовать только один байт заполнение.

Заполнение может быть проблемой по двум причинам:

  1. В зависимости от компьютерной архитектуры он может различаться. Таким образом, если вы планируете запишите файл на ESP и прочитайте его на своем ПК (или наоборот). наоборот), это один из возможных источников несовместимости, другие — порядок следования байтов и размеры элементов.

  2. Это пустая трата места на диске, хотя это может не быть проблемой, если вы теряете только 1 байт из 20.

Если заполнение может быть проблематичным, единственным допустимым вариантом является запись и читать структуру по одному члену за раз. И наоборот, если файл только когда-либо будут доступны для ESP, и вы не против потерять несколько процент файла до заполнения, тогда не волнуйтесь, следуйте совету Томаса, и читайте и записывайте структуру как массив байтов: это просто проще.

,

спасибо за эту точку зрения (это была догадка, которую я получил, комментируя ответ Томаса). У меня есть 3 других файла на этом ESP (может быть ESP32 или ESP8266), в которых хранятся параметры. Другой вариант, о котором я думал, - это сохранить его в виде строкового файла value0;value1;value2... с разделителем в качестве маркера. И, наконец, используя ArduinoJSON в качестве пары ключ/значение, снова сохраните его аналогично файлу параметров, который у меня есть на борту. Чтобы вы посоветовали ?, @Guy . D

Спасибо, я собирался сослаться на отступы как на часть или причину использования sizeof, а не на конкретный номер размера, и хотел указать на это в отношении логического значения, но каким-то образом он потерялся при редактировании, пытаясь не писать слишком много точек. Я признаю, что я IDYer, и пытаюсь написать свои ответы на более простом уровне. И спасибо за улучшение всех моих ответов здесь :-) у вас есть права на редактирование, @Tomas

@guyd: я почти всегда предпочитаю текстовые форматы двоичным: их несколько сложнее анализировать на компьютере, но они намного удобнее для людей, которые собираются отлаживать материал. Двоичный — это выбор, когда вам нужна эффективность использования пространства (либо в самом файле, либо в коде, который его обрабатывает) или производительность ввода-вывода. При этом: все варианты, которые вы предлагаете, действительны. Выберите формат, который вам наиболее удобен., @Edgar Bonet

@Томас: Спасибо! Иногда я придираюсь. Ваши ответы являются полезным и ценным вкладом, и улучшение того, что уже хорошо, кажется весьма полезным. ;-), @Edgar Bonet

Спасибо вам обоим за полный ответ! Парень, @Guy . D


0

структуру можно записать непосредственно в файл SPIFS/FFAT следующим образом:

 * EXAMPLE READING AND WRITING A C++ STRUCT TO A FILE
 */
#include <FS.h>
#include <SPIFFS.h>

typedef struct {
  char someString[10];
  float someFloat;
} MyStruct;

MyStruct myStruct = { "test test",1.7 };

void setup() {
  // поместите сюда код установки для однократного запуска:
  Serial.begin(115200);
  
  if(!SPIFFS.begin()){
      Serial.println("Card Mount Failed");
      return;
  }

  strncpy( myStruct.someString, "testtesttest", sizeof(myStruct.someString) );
  File myFile = SPIFFS.open("/somefile.txt", FILE_WRITE);
  myFile.write((byte *)&myStruct, sizeof(myStruct));
  myFile.close();
}

void loop() {
  // поместите сюда ваш основной код для многократного запуска:
  File myFile = SPIFFS.open("/somefile.txt", FILE_WRITE);
  myFile.read((byte *)&myStruct, sizeof(myStruct));
  myFile.close();
  Serial.printf( "read: %s, %.2f\n", myStruct.someString, myStruct.someFloat );
  delay(1000);
}

приведенный выше код доступен в этом Github Gist.

,

Придирка: если карта не монтируется, setup() должна abort(), а не return., @Edgar Bonet

Я согласен. Не хватает нескольких проверок кода передовой практики, чтобы это работало безупречно. Still — это звуковой фрагмент кода для использования., @Miguel Silva

@MiguelTomás Предполагаете, что это будет работать так же, используя LittleFS?, @Guy . D

честно говоря, не пробовал в последнее время. однако я не вижу причин, почему это не сработает..., @Miguel Silva

@MiguelTomás - я пытаюсь понять, почему myFile.read((byte *)&myStruct, sizeof(myStruct)); используется (byte *)? Я бы никогда не догадался о таком синтаксисе, @Guy . D

правильно... я могу порекомендовать несколько хороших видеоуроков на YouTube, в качестве альтернативы - coursera.org или udemy, @Miguel Silva