一、前言

最近使用ESP32 WROOM开发语音识别项目时,需要使用阵列麦克风采集信号,并进行切片取出含有语音部分的片段,进而使用base64编码将语音上传到后端服务器进行识别。

在项目开发时,发现虽然ESP32 WROOM有520KB的SRAM,但是还是无法满足语音识别的需求。

查找资料后,发现ESP32-S3N16R8带有8MB的PSRAM,ESP32-S3 PSRAM的配置与测试方法见之前的一篇笔记。

本篇文章首先介绍Arduino环境中常用的Json库ArduinoJson的普通用法,然后介绍联合PSRAM使用的方法。

二、ArduinoJson普通用法

ArduinoJson的安装方法可以看之前的一篇笔记。在库管理器中搜索ArduinoJson并下载即可。

ArduinoJson官方库地址为:https://github.com/bblanchon/ArduinoJson

快速上手:ArduinoJson官方贴心地提供了Json助手,可以根据Json格式自动生成代码:https://arduinojson.org/v6/assistant/#/step1

1. ArduinoJson V6版本库函数

库函数整理引自:finedayforu

esp32 PSRAM 配置 esp32 psram arduino_反序列化

2. 核心类JsonDocument(内存管理)

JsonDocument作为整个V6版本ArduinoJson库的内存入口,负责处理整个json数据的内存管理,分为两个实现类:

  • (1)DynamicJsonDocument 内存分配在heap区,无固定大小,可以自动增长所需空间,方法调用完自动回收,建议内存大小大于1KB使用;
DynamicJsonDocument doc(2048); //创建一个DynamicJsonDocument类型的doc对象,大小2048byte
  • (2)StaticJsonDocument StaticJsonDocument,内存分配在stack区,有固定大小,大小值由开发者定义,方法调用完自动回收,建议内存大小小于1KB使用;
StaticJsonDocument<256> doc;

3. 具体使用案例

转自:Naisu Xu

(1)反序列化
  • 引用ArduinoJson库;
  • 声明JsonDocument对象;
  • 尝试反序列化json字符串到JsonDocument对象;
  • 根据需求取用数据;
#include <ArduinoJson.h>

//声明一个json数据
char myJson[] = "{\"myChar\":\"hello\",\"myArray\":[13,14],\"myObject\":{\"myFloat\":3.1415926}}";

void setup()
{
    Serial.begin(115200);
    Serial.println();

    //StaticJsonDocument<200> doc; //声明一个JsonDocument对象
    DynamicJsonDocument doc(200); //声明一个JsonDocument对象

    DeserializationError error = deserializeJson(doc, myJson); //反序列化JSON数据

    // if (error.c_str() == "OK") //检查反序列化是否成功
    if (!error) //检查反序列化是否成功
    {
        //读取json节点
        const char *myC = doc["myChar"]; //等同于const char *myC = doc["myChar"].as<char*>();
        int myN0 = doc["myArray"][0]; //等同于int myN0 = doc["myArray"][0].as<int>();
        int myN1 = doc["myArray"][1]; //等同于int myN1 = doc["myArray"][1].as<int>();
        float myF = doc["myObject"]["myFloat"]; //等同于float myF = doc["myObject"]["myFloat"].as<float>();

        Serial.println(myC);
        Serial.println(myN0);
        Serial.println(myN1);
        Serial.println(myF);
        // Serial.println(myF, n); //n指定输出小数位数
    }
}

void loop()
{
}

esp32 PSRAM 配置 esp32 psram arduino_语音识别_02

(2)序列化
  • 引用ArduinoJson库;
  • 声明JsonDocument对象;
  • 向JsonDocument对象中添加数据;
  • 序列化处理JsonDocument对象使成为json字符串;
#include <ArduinoJson.h>

void setup()
{
    Serial.begin(115200);
    Serial.println();

    //StaticJsonDocument<200> doc; //声明一个JsonDocument对象
    DynamicJsonDocument doc(200); //声明一个JsonDocument对象

    doc["myChar"] = "hello"; //添加一个字符串对象节点

    JsonArray myA = doc.createNestedArray("myArray"); //添加一个数组节点
    myA.add(true);                                    //使用add方式添加数据到JsonArray
    myA.add(false);

    JsonObject myO = doc.createNestedObject("myObject"); //添加一个对象节点
    myO["myNumber"] = 1234567890;                        //使用[]添加数据到JsonObject

    serializeJson(doc, Serial); //序列化JSON数据(压缩形式),并从Serial输出
    // 以下三行输出结果同上面一行
    // char myDoc[measureJson(doc) + 1];
    // serializeJson(doc, myDoc, measureJson(doc) + 1);
    // Serial.println(myDoc);

    Serial.println();
    Serial.println();

    serializeJsonPretty(doc, Serial); //序列化JSON数据(展开形式),并从Serial输出
    // 以下三行输出结果同上面一行
    // char myDocP[measureJsonPretty(doc) + 1];
    // serializeJsonPretty(doc, myDocP, measureJsonPretty(doc) + 1);
    // Serial.println(myDocP);
}

void loop()
{
}

esp32 PSRAM 配置 esp32 psram arduino_反序列化_03

三、ArduinoJson联合PSRAM用法

ArduinoJson官方对PSRAM的介绍见:https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/

ArduinoJson中默认使用malloc()函数分配SRAM空间;而使用PSRAM需要使用heap_caps_malloc(MALLOC_CAP_SPIRAM)函数分配空间。因此,要联合PSRAM使用,就不能像上面一样直接调用DynamicJsonDocument

下面是具体实现案例:

struct SpiRamAllocator {
  void* allocate(size_t size) {
    return heap_caps_malloc(size, MALLOC_CAP_SPIRAM);
  }

  void deallocate(void* pointer) {
    heap_caps_free(pointer);
  }

  void* reallocate(void* ptr, size_t new_size) {
    return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM);
  }
};

using SpiRamJsonDocument = BasicJsonDocument<SpiRamAllocator>;

这个片段定义的SpiRamJsonDocument类的使用方法与JsonDocument一致:

SpiRamJsonDocument doc(1048576);
deserializeJson(doc, input);

注意事项:

  1. 不能声明一个全局变量SpiRamJsonDocument,因为它会heap_caps_malloc()在 PSRAM 准备好使用之前调用。
  2. 需要开启PSRAM,否则ESP32-S3会无限重启。开启方法见之前的一篇笔记。
  3. ArduinoJson V5和V5并不兼容,注意检查版本为V6。