ESPAsyncWebServer 从 RAM 服务大型阵列
ESPAsyncWebServer serve large array from RAM
我正在尝试使用用于 ArduinoIDE 的 ESPAsyncWebServer 库从 ESP32 堆提供一个包含 8192 个值的大型浮点数组。 µC 是一个 ESP32 devkit c,我想用浏览器访问数组。
这是数组的代码:
#include "AsyncJson.h"
#include "ArduinoJson.h"
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#define SAMPLES 8192
static float * vReal;
void setup() {
vReal = (float *) malloc(SAMPLES * sizeof(float));
assert(vReal != NULL);
}
void loop() {
//Put something into the array
for (int i = 0; i < SAMPLES; i++)
{
vReal[i] = 1.1;
}
}
目前我使用 "ArduinoJson Basic Response" 并以 512 个值的一部分发送大数组。对于 1024 个值,如果我尝试使用浏览器访问数组,我会在任务 async_tcp 中出现堆栈溢出,因此我将其设置为 512 个值。这是此代码:
server.on("/array1", HTTP_GET, [](AsyncWebServerRequest * request) {
AsyncResponseStream *response = request->beginResponseStream("application/json");
const size_t CAPACITY = JSON_ARRAY_SIZE(512); //Compute size of array
StaticJsonDocument<CAPACITY> vRealPart;
JsonArray array = vRealPart.to<JsonArray>();
for (int i = 0; i < 512; i++)
{
vRealPart.add(vReal[i]);
}
serializeJson(vRealPart, *response); //Print to HTML
request->send(response);
});
我这样做了 16 次以服务于整个阵列。后来,我用 JavaScript 调用路径的“/array1”、“/array2”、“/array3”...并解析 JSON。这是输出,如果在网络浏览器中调用路径之一:
[0.334593,0.427480,0.181299,0.066654,0.271184,0.356220,0.374454,0.235625,...]
到目前为止,这在大部分时间都有效,但我认为它非常冗长。如果整个数组只有一条路径,那就太好了。它适用于 SPIFFS 的静态文件,例如:
server.serveStatic("/jsonArray1", SPIFFS, "/jsonArray1");
但是将整个阵列写入闪存需要花费很多时间。虽然阅读速度真的很快。
我也尝试了 "ArduinoJson Advanced Response",但我无法使用 JsonArray 将其设置为 运行。不幸的是,来自 ESPAsyncWebServer 的 ArduinoJson GitHub 页面上的示例已被弃用,因为它们在新版本 (v6) 中稍微更改了语法。
简而言之:使用 ESPAsyncWebServer 库从 ESP32 堆为如此大的数组提供服务的最佳方式是什么?目标是稍后在网络浏览器中使用 JavaScript 处理数组。
感谢您的帮助!
本蒂德姆
PS:如果有帮助:我正在使用库 arduinoFFT 从电机电流进行 FFT。因此,我做了一个 ADC,并将模数转换器的 16384 个值保存在一个数组中。该数组被传递到 FFT 库。输出是一个包含 8192 个值的数组。此结果在堆中,应传输到我的 Web 界面以显示结果。我想将数组保留在 RAM 中以提高速度。对于每个测量,您都会得到一个包含 8192 个值的结果数组。
这里有一个关于如何使用编译成闪存的数组的建议:
const uint16_t SAMPLESPERSEND = 8196;
const uint8_t SAMPLESNUMSIZE = 8; // as your nums are 0.271184 so 8 chars
float myMeasureVals [SAMPLESPERSEND];
char myValueArray[SAMPLESPERSEND * (SAMPLESNUMSIZE + 1) + 2 + 1] = {'[=10=]'};
char numBufferArray[SAMPLESNUMSIZE + 1] = {'[=10=]'}; // Converter helper array
然后您添加该代码值
server.on("/array", HTTP_GET, [](AsyncWebServerRequest * request) {
AsyncResponseStream *response = request->beginResponseStream("application/json");
strcpy (myValueArray, "[");
for (uint16_t i = 0; i < SAMPLESPERSEND; i++) {
if (i!=0) strcat (myValueArray, ",");
// float myTempValue = function2GetValues(); // direct froma function
float myTempValue = myMeasureVals [i];
// dtostrf(floatvar, StringLengthIncDecimalPoint, numVarsAfterDecimal, charbuf);
dtostrf(myTempValue, 2, 6, numBufferArray);
strcat (myValueArray, numBufferArray);
}
strcat (myValueArray, "]");
myValueArray [strlen(myValueArray)] = '[=11=]'; // terminate correct
Serial.println(myValueArray); // Only debug
request->send(myValueArray);
myValueArray[0] = '[=11=]'; // empty array for next part
});
代码编译通过,没测试发送可以这样处理还是必须用chunked方法。通常我只在设置和从文件系统读取时才使用这么大的数组。这里处理的是 ArduinoJson 所做的 "same",但是如果您需要长期稳定的 运行 应用程序,只需几行代码并且没有堆碎片。
EDIT
我首选的解决方案是不断写入 SPIFFS 中的文件(请参阅 SD 示例数据记录器或 this post)并提供该文件。对于 ~10.000 个值,这是最干净的方法。上面的代码显示了如何添加括号和逗号,因此该文件的内容是一个大 JSON,可以通过 javascript 快速处理。通过使用例如 10 个不同的日志文件,您可以在出现网络错误或类似错误时恢复
我现在终于找到了一个使用分块响应的解决方案。 ArduinoJson 也不再使用了。
我不得不缩小缓冲区以使其稳定 (maxLen = maxLen >> 1;
)。图书馆给了我一个错误的最大值。缓冲区长度与 maxLen。这可能是错误。
它比我以前的解决方案快得多,并且运行时不会出现任何 µC 崩溃。具有 16384 个值的数组在 422 毫秒内从 µC 整体传输到网络浏览器。
这是服务器响应的代码:
server.on("/array", HTTP_GET, [](AsyncWebServerRequest * request)
{
indexvReal = 0;
AsyncWebServerResponse* response = request->beginChunkedResponse(contentType,
[](uint8_t* buffer, size_t maxLen, size_t index)
{
maxLen = maxLen >> 1;
size_t len = 0;
if (indexvReal == 0)
{
len += sprintf(((char *)buffer), "[%g", vReal[indexvReal]);
indexvReal++;
}
while (len < (maxLen - FLOATSIZE) && indexvReal < LEN(vReal))
{
len += sprintf(((char *)buffer + len), ",%g", vReal[indexvReal]);
indexvReal++;
}
if (indexvReal == LEN(vReal))
{
len += sprintf(((char *)buffer + len), "]");
indexvReal++;
}
return len;
});
request->send(response);
});
这里有一个完整的例子用于测试:
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
const char *ssid = "...";
const char *password = "...";
int counter_wifi = 0;
AsyncWebServer server(80);
#define LEN(arr) ((int) (sizeof (arr) / sizeof (arr)[0]))
#define SAMPLES 16384
static float vReal[SAMPLES]; //Stack
uint16_t indexvReal = 0;
#define FLOATSIZE 20
const char *contentType = "application/json";
//const char *contentType = "text/plain";
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
counter_wifi++;
if (counter_wifi >= 10) { //after 5 seconds timeout - reset board (on unsucessful connection)
ESP.restart();
}
}
Serial.println("WiFi connected.");
Serial.println("IP Address: ");
Serial.println(WiFi.localIP());
for (int i = 0; i < SAMPLES; i++)
{
vReal[i] = random(20) * 3.2357911;
}
server.on("/array", HTTP_GET, [](AsyncWebServerRequest * request)
{
indexvReal = 0;
AsyncWebServerResponse* response = request->beginChunkedResponse(contentType,
[](uint8_t* buffer, size_t maxLen, size_t index)
{
maxLen = maxLen >> 1;
size_t len = 0;
if (indexvReal == 0)
{
len += sprintf(((char *)buffer), "[%g", vReal[indexvReal]);
indexvReal++;
}
while (len < (maxLen - FLOATSIZE) && indexvReal < LEN(vReal))
{
len += sprintf(((char *)buffer + len), ",%g", vReal[indexvReal]);
indexvReal++;
}
if (indexvReal == LEN(vReal))
{
len += sprintf(((char *)buffer + len), "]");
indexvReal++;
}
return len;
});
request->send(response);
});
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
server.begin();
Serial.println("HTTP server started");
}
void loop() {
// put your main code here, to run repeatedly:
}
我正在尝试使用用于 ArduinoIDE 的 ESPAsyncWebServer 库从 ESP32 堆提供一个包含 8192 个值的大型浮点数组。 µC 是一个 ESP32 devkit c,我想用浏览器访问数组。 这是数组的代码:
#include "AsyncJson.h"
#include "ArduinoJson.h"
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#define SAMPLES 8192
static float * vReal;
void setup() {
vReal = (float *) malloc(SAMPLES * sizeof(float));
assert(vReal != NULL);
}
void loop() {
//Put something into the array
for (int i = 0; i < SAMPLES; i++)
{
vReal[i] = 1.1;
}
}
目前我使用 "ArduinoJson Basic Response" 并以 512 个值的一部分发送大数组。对于 1024 个值,如果我尝试使用浏览器访问数组,我会在任务 async_tcp 中出现堆栈溢出,因此我将其设置为 512 个值。这是此代码:
server.on("/array1", HTTP_GET, [](AsyncWebServerRequest * request) {
AsyncResponseStream *response = request->beginResponseStream("application/json");
const size_t CAPACITY = JSON_ARRAY_SIZE(512); //Compute size of array
StaticJsonDocument<CAPACITY> vRealPart;
JsonArray array = vRealPart.to<JsonArray>();
for (int i = 0; i < 512; i++)
{
vRealPart.add(vReal[i]);
}
serializeJson(vRealPart, *response); //Print to HTML
request->send(response);
});
我这样做了 16 次以服务于整个阵列。后来,我用 JavaScript 调用路径的“/array1”、“/array2”、“/array3”...并解析 JSON。这是输出,如果在网络浏览器中调用路径之一:
[0.334593,0.427480,0.181299,0.066654,0.271184,0.356220,0.374454,0.235625,...]
到目前为止,这在大部分时间都有效,但我认为它非常冗长。如果整个数组只有一条路径,那就太好了。它适用于 SPIFFS 的静态文件,例如:
server.serveStatic("/jsonArray1", SPIFFS, "/jsonArray1");
但是将整个阵列写入闪存需要花费很多时间。虽然阅读速度真的很快。
我也尝试了 "ArduinoJson Advanced Response",但我无法使用 JsonArray 将其设置为 运行。不幸的是,来自 ESPAsyncWebServer 的 ArduinoJson GitHub 页面上的示例已被弃用,因为它们在新版本 (v6) 中稍微更改了语法。
简而言之:使用 ESPAsyncWebServer 库从 ESP32 堆为如此大的数组提供服务的最佳方式是什么?目标是稍后在网络浏览器中使用 JavaScript 处理数组。
感谢您的帮助!
本蒂德姆
PS:如果有帮助:我正在使用库 arduinoFFT 从电机电流进行 FFT。因此,我做了一个 ADC,并将模数转换器的 16384 个值保存在一个数组中。该数组被传递到 FFT 库。输出是一个包含 8192 个值的数组。此结果在堆中,应传输到我的 Web 界面以显示结果。我想将数组保留在 RAM 中以提高速度。对于每个测量,您都会得到一个包含 8192 个值的结果数组。
这里有一个关于如何使用编译成闪存的数组的建议:
const uint16_t SAMPLESPERSEND = 8196;
const uint8_t SAMPLESNUMSIZE = 8; // as your nums are 0.271184 so 8 chars
float myMeasureVals [SAMPLESPERSEND];
char myValueArray[SAMPLESPERSEND * (SAMPLESNUMSIZE + 1) + 2 + 1] = {'[=10=]'};
char numBufferArray[SAMPLESNUMSIZE + 1] = {'[=10=]'}; // Converter helper array
然后您添加该代码值
server.on("/array", HTTP_GET, [](AsyncWebServerRequest * request) {
AsyncResponseStream *response = request->beginResponseStream("application/json");
strcpy (myValueArray, "[");
for (uint16_t i = 0; i < SAMPLESPERSEND; i++) {
if (i!=0) strcat (myValueArray, ",");
// float myTempValue = function2GetValues(); // direct froma function
float myTempValue = myMeasureVals [i];
// dtostrf(floatvar, StringLengthIncDecimalPoint, numVarsAfterDecimal, charbuf);
dtostrf(myTempValue, 2, 6, numBufferArray);
strcat (myValueArray, numBufferArray);
}
strcat (myValueArray, "]");
myValueArray [strlen(myValueArray)] = '[=11=]'; // terminate correct
Serial.println(myValueArray); // Only debug
request->send(myValueArray);
myValueArray[0] = '[=11=]'; // empty array for next part
});
代码编译通过,没测试发送可以这样处理还是必须用chunked方法。通常我只在设置和从文件系统读取时才使用这么大的数组。这里处理的是 ArduinoJson 所做的 "same",但是如果您需要长期稳定的 运行 应用程序,只需几行代码并且没有堆碎片。
EDIT
我首选的解决方案是不断写入 SPIFFS 中的文件(请参阅 SD 示例数据记录器或 this post)并提供该文件。对于 ~10.000 个值,这是最干净的方法。上面的代码显示了如何添加括号和逗号,因此该文件的内容是一个大 JSON,可以通过 javascript 快速处理。通过使用例如 10 个不同的日志文件,您可以在出现网络错误或类似错误时恢复
我现在终于找到了一个使用分块响应的解决方案。 ArduinoJson 也不再使用了。
我不得不缩小缓冲区以使其稳定 (maxLen = maxLen >> 1;
)。图书馆给了我一个错误的最大值。缓冲区长度与 maxLen。这可能是错误。
它比我以前的解决方案快得多,并且运行时不会出现任何 µC 崩溃。具有 16384 个值的数组在 422 毫秒内从 µC 整体传输到网络浏览器。
这是服务器响应的代码:
server.on("/array", HTTP_GET, [](AsyncWebServerRequest * request)
{
indexvReal = 0;
AsyncWebServerResponse* response = request->beginChunkedResponse(contentType,
[](uint8_t* buffer, size_t maxLen, size_t index)
{
maxLen = maxLen >> 1;
size_t len = 0;
if (indexvReal == 0)
{
len += sprintf(((char *)buffer), "[%g", vReal[indexvReal]);
indexvReal++;
}
while (len < (maxLen - FLOATSIZE) && indexvReal < LEN(vReal))
{
len += sprintf(((char *)buffer + len), ",%g", vReal[indexvReal]);
indexvReal++;
}
if (indexvReal == LEN(vReal))
{
len += sprintf(((char *)buffer + len), "]");
indexvReal++;
}
return len;
});
request->send(response);
});
这里有一个完整的例子用于测试:
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
const char *ssid = "...";
const char *password = "...";
int counter_wifi = 0;
AsyncWebServer server(80);
#define LEN(arr) ((int) (sizeof (arr) / sizeof (arr)[0]))
#define SAMPLES 16384
static float vReal[SAMPLES]; //Stack
uint16_t indexvReal = 0;
#define FLOATSIZE 20
const char *contentType = "application/json";
//const char *contentType = "text/plain";
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
counter_wifi++;
if (counter_wifi >= 10) { //after 5 seconds timeout - reset board (on unsucessful connection)
ESP.restart();
}
}
Serial.println("WiFi connected.");
Serial.println("IP Address: ");
Serial.println(WiFi.localIP());
for (int i = 0; i < SAMPLES; i++)
{
vReal[i] = random(20) * 3.2357911;
}
server.on("/array", HTTP_GET, [](AsyncWebServerRequest * request)
{
indexvReal = 0;
AsyncWebServerResponse* response = request->beginChunkedResponse(contentType,
[](uint8_t* buffer, size_t maxLen, size_t index)
{
maxLen = maxLen >> 1;
size_t len = 0;
if (indexvReal == 0)
{
len += sprintf(((char *)buffer), "[%g", vReal[indexvReal]);
indexvReal++;
}
while (len < (maxLen - FLOATSIZE) && indexvReal < LEN(vReal))
{
len += sprintf(((char *)buffer + len), ",%g", vReal[indexvReal]);
indexvReal++;
}
if (indexvReal == LEN(vReal))
{
len += sprintf(((char *)buffer + len), "]");
indexvReal++;
}
return len;
});
request->send(response);
});
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
server.begin();
Serial.println("HTTP server started");
}
void loop() {
// put your main code here, to run repeatedly:
}