Skip to content

Commit 679bb39

Browse files
committed
Serialize fxdata without ArduinoJSON
Eliminates size limit. Fixes wled#5458
1 parent b293db6 commit 679bb39

1 file changed

Lines changed: 45 additions & 20 deletions

File tree

wled00/json.cpp

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,21 +1160,6 @@ void serializePins(JsonObject root)
11601160
}
11611161
}
11621162

1163-
// deserializes mode data string into JsonArray
1164-
void serializeModeData(JsonArray fxdata)
1165-
{
1166-
char lineBuffer[256];
1167-
for (size_t i = 0; i < strip.getModeCount(); i++) {
1168-
strncpy_P(lineBuffer, strip.getModeData(i), sizeof(lineBuffer)/sizeof(char)-1);
1169-
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
1170-
if (lineBuffer[0] != 0) {
1171-
char* dataPtr = strchr(lineBuffer,'@');
1172-
if (dataPtr) fxdata.add(dataPtr+1);
1173-
else fxdata.add("");
1174-
}
1175-
}
1176-
}
1177-
11781163
// deserializes mode names string into JsonArray
11791164
// also removes effect data extensions (@...) from deserialised names
11801165
void serializeModeNames(JsonArray arr)
@@ -1191,6 +1176,48 @@ void serializeModeNames(JsonArray arr)
11911176
}
11921177
}
11931178

1179+
// Generate a streamed JSON response for the mode data
1180+
// This uses sendChunked to send the reply in blocks based on how much fit in the outbound
1181+
// packet buffer, minimizing the required state (ie. just the next index to send). This
1182+
// allows us to send an arbitrarily large response without using any significant amount of
1183+
// memory (so no worries about buffer limits).
1184+
void respondModeData(AsyncWebServerRequest* request) {
1185+
size_t fx_index = 0;
1186+
request->sendChunked(FPSTR(CONTENT_TYPE_JSON),
1187+
[fx_index](uint8_t* data, size_t len, size_t) mutable {
1188+
size_t bytes_written = 0;
1189+
char lineBuffer[256];
1190+
while (fx_index < strip.getModeCount() && (len > 5)) {
1191+
strncpy_P(lineBuffer, strip.getModeData(fx_index), sizeof(lineBuffer)/sizeof(char)-1); // Copy to stack buffer for strchr
1192+
if (lineBuffer[0] != 0) {
1193+
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
1194+
char* dataPtr = strchr(lineBuffer,'@'); // Find '@', if there is one
1195+
size_t mode_bytes;
1196+
if (dataPtr) {
1197+
mode_bytes = snprintf_P((char*) data, len, PSTR(",\"%s\""), dataPtr + 1);
1198+
if (mode_bytes > len) break; // didn't fit; break loop and try again next packet
1199+
} else {
1200+
strncpy_P((char*)data, PSTR(",\"\""), len);
1201+
mode_bytes = 3;
1202+
}
1203+
if (fx_index == 0) *data = '[';
1204+
data += mode_bytes;
1205+
len -= mode_bytes;
1206+
bytes_written += mode_bytes;
1207+
}
1208+
++fx_index;
1209+
}
1210+
1211+
if ((fx_index == strip.getModeCount()) && (len >= 1)) {
1212+
*data = ']';
1213+
++bytes_written;
1214+
++fx_index; // we're really done
1215+
}
1216+
1217+
return bytes_written;
1218+
});
1219+
}
1220+
11941221
// Global buffer locking response helper class (to make sure lock is released when AsyncJsonResponse is destroyed)
11951222
class LockedJsonResponse: public AsyncJsonResponse {
11961223
bool _holding_lock;
@@ -1218,7 +1245,7 @@ class LockedJsonResponse: public AsyncJsonResponse {
12181245
void serveJson(AsyncWebServerRequest* request)
12191246
{
12201247
enum class json_target {
1221-
all, state, info, state_info, nodes, effects, palettes, fxdata, networks, config, pins
1248+
all, state, info, state_info, nodes, effects, palettes, networks, config, pins
12221249
};
12231250
json_target subJson = json_target::all;
12241251

@@ -1229,7 +1256,7 @@ void serveJson(AsyncWebServerRequest* request)
12291256
else if (url.indexOf(F("nodes")) > 0) subJson = json_target::nodes;
12301257
else if (url.indexOf(F("eff")) > 0) subJson = json_target::effects;
12311258
else if (url.indexOf(F("palx")) > 0) subJson = json_target::palettes;
1232-
else if (url.indexOf(F("fxda")) > 0) subJson = json_target::fxdata;
1259+
else if (url.indexOf(F("fxda")) > 0) { respondModeData(request); return; }
12331260
else if (url.indexOf(F("net")) > 0) subJson = json_target::networks;
12341261
else if (url.indexOf(F("cfg")) > 0) subJson = json_target::config;
12351262
else if (url.indexOf(F("pins")) > 0) subJson = json_target::pins;
@@ -1254,7 +1281,7 @@ void serveJson(AsyncWebServerRequest* request)
12541281
}
12551282
// releaseJSONBufferLock() will be called when "response" is destroyed (from AsyncWebServer)
12561283
// make sure you delete "response" if no "request->send(response);" is made
1257-
LockedJsonResponse *response = new LockedJsonResponse(pDoc, subJson==json_target::fxdata || subJson==json_target::effects); // will clear and convert JsonDocument into JsonArray if necessary
1284+
LockedJsonResponse *response = new LockedJsonResponse(pDoc, subJson==json_target::effects); // will clear and convert JsonDocument into JsonArray if necessary
12581285

12591286
JsonVariant lDoc = response->getRoot();
12601287

@@ -1270,8 +1297,6 @@ void serveJson(AsyncWebServerRequest* request)
12701297
serializePalettes(lDoc, request->hasParam(F("page")) ? request->getParam(F("page"))->value().toInt() : 0); break;
12711298
case json_target::effects:
12721299
serializeModeNames(lDoc); break;
1273-
case json_target::fxdata:
1274-
serializeModeData(lDoc); break;
12751300
case json_target::networks:
12761301
serializeNetworks(lDoc); break;
12771302
case json_target::config:

0 commit comments

Comments
 (0)