mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-06-14 20:06:27 +03:00
use PSRAM in AsyncJsonResponse
This commit is contained in:
@@ -1337,3 +1337,7 @@ startm
|
||||
netifs
|
||||
testemail
|
||||
sendmail
|
||||
serialises
|
||||
SPIRAM
|
||||
optimisations
|
||||
IILE
|
||||
@@ -10,7 +10,7 @@ APStatus::APStatus(AsyncWebServer * server, SecurityManager * securityManager, A
|
||||
}
|
||||
|
||||
void APStatus::apStatus(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new emsesp::PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["status"] = _apSettingsService->getAPNetworkStatus();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "AuthenticationService.h"
|
||||
|
||||
#include "../core/psram_async_json_response.h"
|
||||
|
||||
AuthenticationService::AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager) {
|
||||
// none of these need authentication
|
||||
@@ -23,7 +25,7 @@ void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant
|
||||
Authentication authentication = _securityManager->authenticate(username, password);
|
||||
if (authentication.authenticated) {
|
||||
User * user = authentication.user;
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new emsesp::PsramAsyncJsonResponse(false);
|
||||
JsonObject jsonObject = response->getRoot();
|
||||
jsonObject["access_token"] = _securityManager->generateJWT(user);
|
||||
response->setLength();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "SecurityManager.h"
|
||||
#include "StatefulService.h"
|
||||
#include "../core/psram_async_json_response.h"
|
||||
|
||||
#define HTTP_ENDPOINT_ORIGIN_ID "http"
|
||||
|
||||
@@ -58,7 +59,7 @@ class HttpEndpoint {
|
||||
}
|
||||
}
|
||||
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new emsesp::PsramAsyncJsonResponse(false);
|
||||
JsonObject jsonObject = response->getRoot().to<JsonObject>();
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
response->setLength();
|
||||
|
||||
@@ -10,7 +10,7 @@ MqttStatus::MqttStatus(AsyncWebServer * server, MqttSettingsService * mqttSettin
|
||||
}
|
||||
|
||||
void MqttStatus::mqttStatus(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new emsesp::PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["enabled"] = _mqttSettingsService->isEnabled();
|
||||
|
||||
@@ -30,7 +30,7 @@ String toLocalTimeString(tm * time) {
|
||||
}
|
||||
|
||||
void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new emsesp::PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// grab the current instant in unix seconds
|
||||
|
||||
@@ -13,7 +13,7 @@ NetworkStatus::NetworkStatus(AsyncWebServer * server, SecurityManager * security
|
||||
}
|
||||
|
||||
void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new emsesp::PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
wl_status_t wifi_status = WiFi.status();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "SecuritySettingsService.h"
|
||||
|
||||
#include "../core/psram_async_json_response.h"
|
||||
|
||||
SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * fs)
|
||||
: _httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this)
|
||||
, _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE)
|
||||
@@ -112,7 +114,7 @@ void SecuritySettingsService::generateToken(AsyncWebServerRequest * request) {
|
||||
auto usernameParam = request->getParam("username");
|
||||
for (const User & _user : _state.users) {
|
||||
if (_user.username == usernameParam->value()) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new emsesp::PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
root["token"] = generateJWT(&_user);
|
||||
response->setLength();
|
||||
|
||||
@@ -189,7 +189,7 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
||||
|
||||
// add MD5 to the response
|
||||
if (strlen(_md5.data()) == _md5.size() - 1) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new emsesp::PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
root["md5"] = _md5.data();
|
||||
response->setLength();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "WiFiScanner.h"
|
||||
|
||||
#include "../core/psram_async_json_response.h"
|
||||
|
||||
WiFiScanner::WiFiScanner(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
securityManager->addEndpoint(server, SCAN_NETWORKS_SERVICE_PATH, AuthenticationPredicates::IS_ADMIN, [this](AsyncWebServerRequest * request) {
|
||||
scanNetworks(request);
|
||||
@@ -22,7 +24,7 @@ void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) {
|
||||
void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
|
||||
const int numNetworks = WiFi.scanComplete();
|
||||
if (numNetworks > -1) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new emsesp::PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
JsonArray networks = root["networks"].to<JsonArray>();
|
||||
for (uint8_t i = 0; i < numNetworks; i++) {
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
#include "../web/WebModulesService.h"
|
||||
|
||||
#include "psram_json_allocator.h"
|
||||
#include "psram_async_json_response.h"
|
||||
#include "emsdevicevalue.h"
|
||||
#include "emsdevice.h"
|
||||
#include "emsfactory.h"
|
||||
|
||||
143
src/core/psram_async_json_response.h
Normal file
143
src/core/psram_async_json_response.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2025 emsesp.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef EMSESP_PSRAM_ASYNC_JSON_RESPONSE_H
|
||||
#define EMSESP_PSRAM_ASYNC_JSON_RESPONSE_H
|
||||
|
||||
#include "psram_json_allocator.h"
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include <AsyncJson.h>
|
||||
#include <ChunkPrint.h>
|
||||
#else
|
||||
#include <AsyncJson.h>
|
||||
#endif
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// AsyncJsonResponse subclass whose JsonDocument lives in PSRAM instead of
|
||||
// internal SRAM.
|
||||
//
|
||||
// Why: every web API response goes through AsyncJsonResponse. The library's
|
||||
// base class declares `JsonDocument _jsonBuffer;` with the *default*
|
||||
// allocator, which on ESP32 means malloc() → internal heap. For large
|
||||
// payloads (Dashboard, /rest/coreData, /rest/sensorData, full settings,
|
||||
// customizations, etc.) this transiently consumes many KB of the same
|
||||
// internal heap that LwIP / AsyncTCP / mbedTLS also need. Each concurrent
|
||||
// browser tab compounds the cost.
|
||||
//
|
||||
// We can't change the base class's _jsonBuffer allocator (the upstream
|
||||
// constructor doesn't take one), but we can route around it: keep our own
|
||||
// PSRAM-backed document, override the virtual setLength()/_fillBuffer() so
|
||||
// the framework serialises *our* document, and name-hide getRoot() so
|
||||
// callers populate *our* document. The base's _jsonBuffer stays empty
|
||||
// (just one root slot, <~32 bytes).
|
||||
//
|
||||
// Callers must use the derived type (or `auto`) when calling getRoot(),
|
||||
// because getRoot() is non-virtual in the base. `request->send(response)`
|
||||
// works as-is because setLength()/_fillBuffer() ARE virtual in the
|
||||
// AsyncAbstractResponse grandparent.
|
||||
//
|
||||
// On standalone the lib_standalone AsyncJsonResponse stub never actually
|
||||
// serves responses, so this whole class still compiles and behaves
|
||||
// identically (allocator falls back to malloc anyway).
|
||||
class PsramAsyncJsonResponse : public ::AsyncJsonResponse {
|
||||
public:
|
||||
explicit PsramAsyncJsonResponse(bool isArray = false)
|
||||
: ::AsyncJsonResponse(isArray)
|
||||
, psram_doc_(PsramJsonAllocator::instance()) {
|
||||
if (isArray) {
|
||||
psram_root_ = psram_doc_.add<JsonArray>();
|
||||
} else {
|
||||
psram_root_ = psram_doc_.add<JsonObject>();
|
||||
}
|
||||
}
|
||||
|
||||
// Hides AsyncJsonResponse::getRoot(). Must be called through a
|
||||
// derived-type pointer/reference (the framework's base pointer keeps
|
||||
// pointing at the empty base _jsonBuffer, which is intentional).
|
||||
JsonVariant getRoot() {
|
||||
return psram_root_;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
size_t setLength() override {
|
||||
_contentLength = measureJson(psram_root_);
|
||||
if (_contentLength) {
|
||||
_isValid = true;
|
||||
}
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
size_t _fillBuffer(uint8_t * data, size_t len) override {
|
||||
ChunkPrint dest(data, _sentLength, len);
|
||||
serializeJson(psram_root_, dest);
|
||||
return dest.written();
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
JsonDocument psram_doc_;
|
||||
JsonVariant psram_root_;
|
||||
};
|
||||
|
||||
#if !defined(EMSESP_STANDALONE) && defined(ASYNC_MSG_PACK_SUPPORT) && ASYNC_MSG_PACK_SUPPORT == 1
|
||||
// MessagePack equivalent — same routing trick but serialises with MsgPack.
|
||||
class PsramAsyncMessagePackResponse : public ::AsyncMessagePackResponse {
|
||||
public:
|
||||
explicit PsramAsyncMessagePackResponse(bool isArray = false)
|
||||
: ::AsyncMessagePackResponse(isArray)
|
||||
, psram_doc_(PsramJsonAllocator::instance()) {
|
||||
if (isArray) {
|
||||
psram_root_ = psram_doc_.add<JsonArray>();
|
||||
} else {
|
||||
psram_root_ = psram_doc_.add<JsonObject>();
|
||||
}
|
||||
}
|
||||
|
||||
JsonVariant getRoot() {
|
||||
return psram_root_;
|
||||
}
|
||||
|
||||
size_t setLength() override {
|
||||
_contentLength = measureMsgPack(psram_root_);
|
||||
if (_contentLength) {
|
||||
_isValid = true;
|
||||
}
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
size_t _fillBuffer(uint8_t * data, size_t len) override {
|
||||
ChunkPrint dest(data, _sentLength, len);
|
||||
serializeMsgPack(psram_root_, dest);
|
||||
return dest.written();
|
||||
}
|
||||
|
||||
private:
|
||||
JsonDocument psram_doc_;
|
||||
JsonVariant psram_root_;
|
||||
};
|
||||
#else
|
||||
// Standalone or no msgpack support: alias to plain JSON response so the
|
||||
// codebase compiles unchanged.
|
||||
using PsramAsyncMessagePackResponse = PsramAsyncJsonResponse;
|
||||
#endif
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -108,7 +108,7 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) {
|
||||
EMSESP::system_.refreshHeapMem();
|
||||
|
||||
// output json buffer
|
||||
auto response = new AsyncJsonResponse();
|
||||
auto response = new PsramAsyncJsonResponse();
|
||||
|
||||
// add more mem if needed - won't be needed in ArduinoJson 7
|
||||
// while (!response->getSize()) {
|
||||
|
||||
@@ -27,7 +27,7 @@ WebActivityService::WebActivityService(AsyncWebServer * server, SecurityManager
|
||||
}
|
||||
|
||||
void WebActivityService::webActivityService(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
JsonArray statsJson = root["stats"].to<JsonArray>();
|
||||
|
||||
@@ -58,7 +58,7 @@ WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securi
|
||||
// this is used in the Devices page and contains all EMS device information
|
||||
// /coreData endpoint
|
||||
void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// list is already sorted by device type
|
||||
@@ -70,7 +70,7 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
obj["id"] = emsdevice->unique_id(); // a unique id
|
||||
obj["tn"] = emsdevice->device_type_2_device_name_translated(); // translated device type name
|
||||
obj["t"] = emsdevice->device_type(); // device type number
|
||||
obj["b"] = emsdevice->brand_to_cstr(); // brand
|
||||
obj["b"] = emsdevice->brand_to_char(); // brand (std::string → copied into doc, safe across async serialize)
|
||||
obj["n"] = emsdevice->name(); // custom name
|
||||
obj["d"] = emsdevice->device_id(); // deviceid
|
||||
obj["p"] = emsdevice->product_id(); // productid
|
||||
@@ -104,7 +104,7 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
// sensor data - sends back to web
|
||||
// /sensorData endpoint
|
||||
void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// temperature sensors
|
||||
@@ -176,7 +176,7 @@ void WebDataService::device_data(AsyncWebServerRequest * request) {
|
||||
if (request->hasParam(F_(id))) {
|
||||
id = Helpers::atoint(request->getParam(F_(id))->value().c_str()); // get id from url
|
||||
|
||||
auto * response = new AsyncMessagePackResponse();
|
||||
auto * response = new PsramAsyncMessagePackResponse();
|
||||
|
||||
// check size
|
||||
// while (!response) {
|
||||
@@ -217,6 +217,9 @@ void WebDataService::device_data(AsyncWebServerRequest * request) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// no matching device and not CUSTOM_UID: we never called request->send(response),
|
||||
// so AsyncWebServer never took ownership. Delete it ourselves to avoid leaking.
|
||||
delete response;
|
||||
}
|
||||
|
||||
// invalid
|
||||
@@ -269,7 +272,7 @@ void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVar
|
||||
return;
|
||||
}
|
||||
// create JSON for output
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new PsramAsyncJsonResponse(false);
|
||||
JsonObject output = response->getRoot();
|
||||
// the data could be in any format, but we need string
|
||||
// authenticated is always true
|
||||
|
||||
@@ -193,7 +193,7 @@ void WebLogService::transmit(const QueuedLogMessage & message) {
|
||||
void WebLogService::getSetValues(AsyncWebServerRequest * request, JsonVariant json) {
|
||||
if ((request->method() == HTTP_GET) || (!json.is<JsonObject>())) {
|
||||
// GET - return the values
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
root["level"] = level_;
|
||||
root["max_messages"] = maximum_log_messages_;
|
||||
|
||||
@@ -414,7 +414,7 @@ void WebSettingsService::board_profile(AsyncWebServerRequest * request) {
|
||||
if (request->hasParam("boardProfile")) {
|
||||
std::string board_profile = request->getParam("boardProfile")->value().c_str();
|
||||
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// 0=led, 1=dallas, 2=rx, 3=tx, 4=button, 5=phy_type, 6=eth_power, 7=eth_phy_addr, 8=eth_clock_mode, 9=led_type
|
||||
|
||||
@@ -47,7 +47,7 @@ WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * se
|
||||
void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
||||
EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap
|
||||
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
auto * response = new PsramAsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
//
|
||||
@@ -187,7 +187,7 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
||||
|
||||
// generic action handler - as a POST
|
||||
void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json) {
|
||||
auto * response = new AsyncJsonResponse();
|
||||
auto * response = new PsramAsyncJsonResponse();
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// param is optional - https://arduinojson.org/news/2024/09/18/arduinojson-7-2/
|
||||
|
||||
Reference in New Issue
Block a user