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

问题描述

我有一个大约有1000个对象的“大” JSON文件
使用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,Serial);
  configFile.close();
  Serial.println("");
  Serial.println(" - config.json saved - OK.");

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

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

解决方法

TLDR:手动调平非常困难,并且无论如何也可以自动调平。我会将经常更改的配置选项分离到一个不同的文件中,以便您每次写的次数更少,并且仅在绝对需要时(即:程序结束)写入磁盘

如果json文件的长度更改,即:将“ true”更改为“ false”,则必须重写整个文件,因为必须分配给该文件的[disc]空间现在已更改。 如果为每个配置值保留一定数量的字符,或者仅使用x位数字作为配置值,则可以直接重写磁盘上的二进制数据,而根本不与文件连接。但这将需要一些非常底层的编程,并需要确切的内存地址知识。 另外,由于您正在使用闪存作为存储设备,因此对于所做的每个更改,您仍然必须读取,更新和写入完整的内存块。随着时间的推移,特别是如果您仅以任何规律性更新一个值,与完全重写相比,从长远来看,这可能对磁盘造成更大的破坏。

台式机固态硬盘将自动均衡整个磁盘的磨损。如果esp所做的相同,那么您不必担心自己会变平,而是在重写文件时将文件大小保持为最小。

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

,

处理此问题的最佳方法是分解JSON-将每个顶级对象写入其自己的文件。您已经有了对象的标签,如果您需要更多空间来存放标签名称,请使用这些标签作为以/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的闪存,其中SPIFFS可能有1.5MB的可用闪存。您可以在1.5MB的空间中存储20,000个字节75次-因此,损耗平衡的系数可能约为75倍或更小。

在大多数ESP8266和ESP32中,闪存的额定写入次数为100,000次(实际上,它的使用寿命通常会更长)。因此,通过平均磨损,您可能可以写20,000个字节的文件750,000次,而不会期望看到错误。

您实际上可能会写1000或10,000次此文件吗?

如果是这样-对于配置文件来说,这似乎很奇怪,应该重新考虑如何处理此文件以及它的作用。

否则,这实际上不是问题。