Arduino ESP 编辑大 JSON 文件 - 磨损均衡

Arduino ESP edit big JSON file - wear leveling

我有一个“大”JSON 文件,大约有 1000 个对象。
使用 https://arduinojson.org/ 序列化/反序列化。

void ConfigFile_Save_Variable(String VarName, String VarValue) {
  Serial.print("ConfigFile_Save_Variable: ");
  Serial.print(VarName); Serial.print("="); Serial.print(VarValue);

  File configFile = SPIFFS.open("/config.json", "r");
  if (!configFile) {
    Serial.println("- failed to open config file for writing");
    return;
  }

  DynamicJsonDocument doc(2048);
  deserializeJson(doc, configFile);
  serializeJson(doc, Serial);
  configFile.close();
  //
  doc[VarName] = VarValue;
  //
  configFile = SPIFFS.open("/config.json", "w");
  serializeJson(doc, configFile);
  serializeJson(doc, Serial);
  configFile.close();
  Serial.println("");
  Serial.println(" - config.json saved - OK.");

如果我只想编辑 1 个对象并保存 JSON 文件,则需要重新写入整个文件。
这对磨损均衡不利,因为它会再次写入所有数据,但只有 1 个对象已更改。

所以我正在寻找一种只写入更改而不是整个文件的方法。试过到处找,但运气不好。

TLDR:手动磨损均衡非常困难,反正均衡可能是自动的。我会将经常更改的配置选项分离到一个不同的文件中,这样您每次写的就更少,并且只在绝对必要时才写入磁盘(即:程序结束)

如果 json 文件的长度发生变化,即:您将“true”更改为“false”,则必须重写整个文件,因为 [disc] space 必须分配给该文件现已更改。 如果为每个配置值保留一定数量的字符,或者仅使用 x 位数字作为配置值,则可以直接重写磁盘上的二进制数据,而无需与文件交互。但这将需要一些极低级别的编程,以及确切内存地址的知识。 此外,由于您使用闪存进行存储,您仍然需要为每次更改读取、更新和写入完整的内存块。随着时间的推移,特别是如果您只定期更新一个值,与完全重写相比,这可能对长 运行 中的磁盘造成更大的破坏。

台式机 SSD 会自动平衡整个磁盘的磨损。如果 esp 也这样做,那么您不必担心自己调平磨损,而是在重写时将文件大小保持在最小值。

鉴于上述情况,一种可能的解决方案是将经常重写的配置选项分离到一个单独的文件中,并将很少重写的配置选项保留在主文件中。您还可以将所有配置数据保存在内存中,并且只在程序结束时重写文件,尽管这仅适用于您不经常 start/stop 您的程序的情况。 将分离发挥到极致,理论上您可以为每个配置选项使用一个单独的文件,这样您一次只能重写一个值。

处理此问题的最佳方法是分解 JSON - 将每个顶级对象写入其自己的文件。您已经有了对象的标签,如果您需要更多 space 作为标签名称,请使用这些标签作为前缀为 /config//c/ 的文件名。

让我们看看为什么您向 SPIFF 请求的内容不起作用。

JSON 被序列化以作为文本存储。例如,对象

{
  label_0: {
    label_a: "small string",
    label_b: 1
    },
  label_1: {
    label_c: "another string",
    label_d: "more string"
  }
}

序列化为

{"label_0":{"label_a":"small string","label_b":1},"label_1":{"label_c":"another string","label_d":"more string"}}

SPIFFS 将存储的文件拆分为固定大小的页面。它每次都必须写入整个页面以进行闪烁——它不能只写入更改的字节;闪存不是这样工作的。

假设 label_a 变得更短 - 只是 "s"。然后 label_a 之后的所有内容都必须在文件中向下移动。要让 SPIFFS 执行您要求的操作,它必须编写一个页面并记住该页面只有一部分在使用中。

如果 label_a 的值变大 - "this is a much longer string" 那么文件中的所有内容都必须向上移动。 SPIFFS 将不得不分配一个新页面来存储溢出并记住它只有一部分在使用中。

请记住,SPIFFS 记住哪些块正在使用的唯一方法是将该信息也写入闪存。

对于一个简单的文件系统来说,这是一个很大的要求。

我们也来了解一下情况。文件有多大,写入频率如何?如果您有 1000 个对象,并且每个对象需要 200 个字节来存储,那么您的 JSON 将约为 20,000 个字节。 ESP8266 或 ESP32 平均有 4MB 的闪存,其中可能有 1.5MB 可用于 SPIFFS。您可以在 1.5MB 中存储 20,000 个字节 75 次 - 因此您可能有大约 75 倍或更少的磨损均衡系数。

大多数 ESP8266 和 ESP32 中的闪存额定为 100,000 次写入(实际上它通常会持续更长时间)。因此,使用磨损均衡,您可能会写入一个 20,000 字节的文件 750,000 次而不会看到错误。

您真的有可能将此文件写入 1000 或 10,000 次吗?

如果您是 - 对于配置文件,这似乎是一个奇怪的场景,您应该重新考虑您如何处理这个文件以及它在做什么。

如果不是,那么这实际上不是问题。