一、前言
最近使用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
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()
{
}
(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()
{
}
三、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);
注意事项:
- 不能声明一个全局变量SpiRamJsonDocument,因为它会heap_caps_malloc()在 PSRAM 准备好使用之前调用。
- 需要开启PSRAM,否则ESP32-S3会无限重启。开启方法见之前的一篇笔记。
- ArduinoJson V5和V5并不兼容,注意检查版本为V6。