diff --git a/ESPController/include/DIYBMSServer.h b/ESPController/include/DIYBMSServer.h index 391c57d..95fed54 100644 --- a/ESPController/include/DIYBMSServer.h +++ b/ESPController/include/DIYBMSServer.h @@ -35,6 +35,8 @@ class DIYBMSServer static AsyncWebServer *_myserver; static String UUIDString; + static void PrintStreamComma(AsyncResponseStream *response,const __FlashStringHelper *ifsh, uint32_t value); + static void handleNotFound(AsyncWebServerRequest *request); static void monitor2(AsyncWebServerRequest *request); static void monitor3(AsyncWebServerRequest *request); diff --git a/ESPController/src/DIYBMSServer.cpp b/ESPController/src/DIYBMSServer.cpp index a04b50e..f489b23 100644 --- a/ESPController/src/DIYBMSServer.cpp +++ b/ESPController/src/DIYBMSServer.cpp @@ -308,7 +308,8 @@ void DIYBMSServer::saveRuleConfiguration(AsyncWebServerRequest *request) if (request->hasParam(name, true)) { AsyncWebParameter *p1 = request->getParam(name, true); - mysettings.rulerelaystate[rule][i] = p1->value().equals("X") ? RELAY_X : p1->value().equals("On") ? RelayState::RELAY_ON : RelayState::RELAY_OFF; + mysettings.rulerelaystate[rule][i] = p1->value().equals("X") ? RELAY_X : p1->value().equals("On") ? RelayState::RELAY_ON + : RelayState::RELAY_OFF; } } @@ -686,6 +687,9 @@ void DIYBMSServer::settings(AsyncWebServerRequest *request) settings["MinutesTimeZone"] = mysettings.minutesTimeZone; settings["DST"] = mysettings.daylight; + settings["FreeHeap"] = ESP.getFreeHeap(); + settings["FreeBlockSize"] = ESP.getMaxFreeBlockSize(); + #if defined(ESP8266) settings["now"] = now(); #endif @@ -817,188 +821,340 @@ void DIYBMSServer::handleRestartController(AsyncWebServerRequest *request) void DIYBMSServer::monitor3(AsyncWebServerRequest *request) { - DynamicJsonDocument doc(maximum_controller_cell_modules * 50); - + //DynamicJsonDocument doc(maximum_controller_cell_modules * 50); AsyncResponseStream *response = request->beginResponseStream("application/json"); - //This service exists to ensure that monitor2 is keep as small as possible - //for ESP8266 memory limitations, these values are only updated very infrequently (lazy timer) - JsonArray badpacket = doc.createNestedArray("badpacket"); - JsonArray balancecurrentcount = doc.createNestedArray("balcurrent"); - JsonArray packetreceivedcount = doc.createNestedArray("pktrecvd"); + uint8_t totalModules = mysettings.totalNumberOfBanks * mysettings.totalNumberOfSeriesModules; + uint8_t comma = totalModules - 1; - //doc["FreeHeap"] = ESP.getFreeHeap(); - //doc["FreeBlockSize"] = ESP.getMaxFreeBlockSize(); + response->print("{\"badpacket\":["); - uint8_t totalModules = mysettings.totalNumberOfBanks * mysettings.totalNumberOfSeriesModules; for (uint8_t i = 0; i < totalModules; i++) { if (cmi[i].valid) { - //Just for debug, move these to config packets instead - balancecurrentcount.add(cmi[i].BalanceCurrentCount); - packetreceivedcount.add(cmi[i].PacketReceivedCount); - badpacket.add(cmi[i].badPacketCount); + response->print(cmi[i].badPacketCount); } else { //Return NULL - balancecurrentcount.add((char *)0); - packetreceivedcount.add((char *)0); - badpacket.add((char *)0); + response->print("null"); + } + if (i < comma) + { + response->print(','); } } - serializeJson(doc, *response); + response->print("],\"balcurrent\":["); + + for (uint8_t i = 0; i < totalModules; i++) + { + if (cmi[i].valid) + { + response->print(cmi[i].BalanceCurrentCount); + } + else + { + //Return NULL + response->print("null"); + } + if (i < comma) + { + response->print(','); + } + } + + response->print("],\"pktrecvd\":["); + + for (uint8_t i = 0; i < totalModules; i++) + { + if (cmi[i].valid) + { + response->print(cmi[i].PacketReceivedCount); + } + else + { + //Return NULL + response->print("null"); + } + if (i < comma) + { + response->print(','); + } + } + response->print("]}"); + request->send(response); } +void DIYBMSServer::PrintStreamComma(AsyncResponseStream *response, const __FlashStringHelper *ifsh, uint32_t value) +{ + response->print(ifsh); + response->print(value); + response->print(','); +} + void DIYBMSServer::monitor2(AsyncWebServerRequest *request) { - DynamicJsonDocument doc(maximum_controller_cell_modules * 140); + uint8_t totalModules = mysettings.totalNumberOfBanks * mysettings.totalNumberOfSeriesModules; + const char comma = ','; + const char *null = "null"; - if (doc.capacity() == 0) + AsyncResponseStream *response = request->beginResponseStream("application/json"); + + PrintStreamComma(response, F("{\"banks\":"), mysettings.totalNumberOfBanks); + PrintStreamComma(response, F("\"seriesmodules\":"), mysettings.totalNumberOfSeriesModules); + PrintStreamComma(response, F("\"sent\":"), prg.packetsGenerated); + PrintStreamComma(response, F("\"received\":"), receiveProc.packetsReceived); + PrintStreamComma(response, F("\"modulesfnd\":"), receiveProc.totalModulesFound); + PrintStreamComma(response, F("\"badcrc\":"), receiveProc.totalCRCErrors); + PrintStreamComma(response, F("\"ignored\":"), receiveProc.totalNotProcessedErrors); + PrintStreamComma(response, F("\"roundtrip\":"), receiveProc.packetTimerMillisecond); + PrintStreamComma(response, F("\"oos\":"), receiveProc.totalOutofSequenceErrors); + + response->print(F("\"errors\":[")); + for (size_t i = 0; i < sizeof(rules.ErrorCodes); i++) { - //If memory allocation fails, swap to a small JSON document - //so the interface can report the error. - AsyncResponseStream *response = request->beginResponseStream("application/json"); - DynamicJsonDocument doc2(512); - - doc2["banks"] = mysettings.totalNumberOfBanks; - doc2["seriesmodules"] = mysettings.totalNumberOfSeriesModules; - JsonArray errors = doc2.createNestedArray("errors"); - //JsonArray warnings = doc2.createNestedArray("warnings"); - errors.add(InternalErrorCode::ControllerMemoryError); - doc2["sent"] = prg.packetsGenerated; - doc2["received"] = receiveProc.packetsReceived; - doc2["modulesfnd"] = receiveProc.totalModulesFound; - doc2["badcrc"] = receiveProc.totalCRCErrors; - doc2["ignored"] = receiveProc.totalNotProcessedErrors; - doc2["roundtrip"] = receiveProc.packetTimerMillisecond; - doc2["oos"] = receiveProc.totalOutofSequenceErrors; - - serializeJson(doc2, *response); - request->send(response); + if (rules.ErrorCodes[i] != InternalErrorCode::NoError) + { + //Comma if not zero + if (i) + response->print(comma); + + response->print(rules.ErrorCodes[i]); + } } - else + + response->print("],"); + + response->print(F("\"warnings\":[")); + for (size_t i = 0; i < sizeof(rules.WarningCodes); i++) { - AsyncResponseStream *response = request->beginResponseStream("application/json"); + if (rules.WarningCodes[i] != InternalWarningCode::NoWarning) + { + //Comma if not zero + if (i) + response->print(comma); - doc["banks"] = mysettings.totalNumberOfBanks; - doc["seriesmodules"] = mysettings.totalNumberOfSeriesModules; - JsonArray errors = doc.createNestedArray("errors"); - for (size_t i = 0; i < sizeof(rules.ErrorCodes); i++) + response->print(rules.WarningCodes[i]); + } + } + response->print("],"); + + //voltages + response->print(F("\"voltages\":[")); + + for (uint8_t i = 0; i < totalModules; i++) + { + //Comma if not zero + if (i) + response->print(comma); + + if (cmi[i].valid) { - if (rules.ErrorCodes[i] != InternalErrorCode::NoError) - { - errors.add(rules.ErrorCodes[i]); - } + response->print(cmi[i].voltagemV); } + else + { + //Module is not yet valid so return null values... + response->print(null); + } + } + response->print("],"); + + response->print(F("\"minvoltages\":[")); + + for (uint8_t i = 0; i < totalModules; i++) + { + //Comma if not zero + if (i) + response->print(comma); - JsonArray warnings = doc.createNestedArray("warnings"); - for (size_t i = 0; i < sizeof(rules.WarningCodes); i++) + if (cmi[i].valid) { - if (rules.WarningCodes[i] != InternalWarningCode::NoWarning) - { - warnings.add(rules.WarningCodes[i]); - } + response->print(cmi[i].voltagemVMin); + } + else + { + //Module is not yet valid so return null values... + response->print(null); } + } + response->print("],"); - doc["sent"] = prg.packetsGenerated; - doc["received"] = receiveProc.packetsReceived; - doc["modulesfnd"] = receiveProc.totalModulesFound; - doc["badcrc"] = receiveProc.totalCRCErrors; - doc["ignored"] = receiveProc.totalNotProcessedErrors; - doc["roundtrip"] = receiveProc.packetTimerMillisecond; - doc["oos"] = receiveProc.totalOutofSequenceErrors; + //maxvoltages - uint8_t totalModules = mysettings.totalNumberOfBanks * mysettings.totalNumberOfSeriesModules; + response->print(F("\"maxvoltages\":[")); + + for (uint8_t i = 0; i < totalModules; i++) + { + //Comma if not zero + if (i) + response->print(comma); + + if (cmi[i].valid) + { + response->print(cmi[i].voltagemVMax); + } + else + { + //Module is not yet valid so return null values... + response->print(null); + } + } + response->print("]"); - JsonArray voltages = doc.createNestedArray("voltages"); + response->print(comma); - JsonArray minvoltages = doc.createNestedArray("minvoltages"); - JsonArray maxvoltages = doc.createNestedArray("maxvoltages"); + //inttemp + response->print(F("\"inttemp\":[")); - JsonArray bypass = doc.createNestedArray("bypass"); - JsonArray bypasshot = doc.createNestedArray("bypasshot"); - JsonArray inttemp = doc.createNestedArray("inttemp"); - JsonArray exttemp = doc.createNestedArray("exttemp"); - JsonArray bypasspwm = doc.createNestedArray("bypasspwm"); + for (uint8_t i = 0; i < totalModules; i++) + { + //Comma if not zero + if (i) + response->print(comma); - for (uint8_t i = 0; i < totalModules; i++) + if (cmi[i].valid && cmi[i].internalTemp != -40) { - if (cmi[i].valid) - { - voltages.add(cmi[i].voltagemV); + response->print(cmi[i].internalTemp); + } + else + { + //Module is not yet valid so return null values... + response->print(null); + } + } + response->print("]"); - if (totalModules <= 64) - { - //To preserve memory, only return these parameters when there are less than =64 modules - minvoltages.add(cmi[i].voltagemVMin); - maxvoltages.add(cmi[i].voltagemVMax); - } + response->print(comma); - if (cmi[i].internalTemp != -40) - { - inttemp.add(cmi[i].internalTemp); - } - else - { - inttemp.add((char *)0); - } + //exttemp + response->print(F("\"exttemp\":[")); - if (cmi[i].externalTemp != -40) - { - exttemp.add(cmi[i].externalTemp); - } - else - { - exttemp.add((char *)0); - } + for (uint8_t i = 0; i < totalModules; i++) + { + //Comma if not zero + if (i) + response->print(comma); - bypasspwm.add(cmi[i].inBypass ? cmi[i].PWMValue : 0); - //Convert boolean to 1 or 0 to save bandwidth (every byte counts on this request) - bypass.add(cmi[i].inBypass ? 1 : 0); - bypasshot.add(cmi[i].bypassOverTemp ? 1 : 0); - } - else - { - //Module is not yet valid so return null values... - voltages.add((char *)0); - if (totalModules <= 64) - { - minvoltages.add((char *)0); - maxvoltages.add((char *)0); - //badpacket.add(0); - } - inttemp.add((char *)0); - exttemp.add((char *)0); - bypasspwm.add(0); - //Convert boolean to 1 or 0 to save bandwidth (every byte counts on this request) - bypass.add(0); - bypasshot.add(0); - } + if (cmi[i].valid && cmi[i].externalTemp != -40) + { + response->print(cmi[i].externalTemp); } + else + { + //Module is not yet valid so return null values... + response->print(null); + } + } + response->print(']'); + + response->print(comma); + + //bypass + response->print(F("\"bypass\":[")); + + for (uint8_t i = 0; i < totalModules; i++) + { + //Comma if not zero + if (i) + response->print(comma); - JsonArray bankvoltage = doc.createNestedArray("bankv"); - JsonArray voltagerange = doc.createNestedArray("voltrange"); - for (uint8_t b = 0; b < mysettings.totalNumberOfBanks; b++) + if (cmi[i].valid && cmi[i].inBypass) { - bankvoltage.add(rules.packvoltage[b]); - voltagerange.add(rules.VoltageRangeInBank(b)); + response->print('1'); } + else + { + response->print('0'); + } + } + response->print("]"); - //Current reading in mA - JsonArray current = doc.createNestedArray("current"); - //current.add(10000); - //NULL - current.add((char *)0); + response->print(comma); - response->addHeader("Cache-Control", "no-store"); + //bypasshot + response->print(F("\"bypasshot\":[")); - serializeJson(doc, *response); - request->send(response); + for (uint8_t i = 0; i < totalModules; i++) + { + //Comma if not zero + if (i) + response->print(comma); + + if (cmi[i].valid && cmi[i].bypassOverTemp) + { + response->print('1'); + } + else + { + response->print('0'); + } } + response->print(']'); + + response->print(comma); + + //bypasspwm + response->print(F("\"bypasspwm\":[")); + + for (uint8_t i = 0; i < totalModules; i++) + { + //Comma if not zero + if (i) + response->print(comma); + + if (cmi[i].valid && cmi[i].inBypass) + { + response->print(cmi[i].PWMValue); + } + else + { + response->print('0'); + } + } + response->print(']'); + + response->print(comma); + + //bypasspwm + response->print(F("\"bankv\":[")); + + for (uint8_t i = 0; i < mysettings.totalNumberOfBanks; i++) + { + //Comma if not zero + if (i) + response->print(comma); + + response->print(rules.packvoltage[i]); + } + response->print("]"); + + response->print(comma); + + //bypasspwm + response->print(F("\"voltrange\":[")); + + for (uint8_t i = 0; i < mysettings.totalNumberOfBanks; i++) + { + //Comma if not zero + if (i) + response->print(comma); + + response->print(rules.VoltageRangeInBank(i)); + } + response->print("]"); + + response->print(comma); + response->print(F("\"current\":[")); + response->print(null); + response->print("]"); + + //The END... + response->print('}'); + request->send(response); } String DIYBMSServer::TemplateProcessor(const String &var) diff --git a/ESPController/src/main.cpp b/ESPController/src/main.cpp index e2e3635..6a57b01 100644 --- a/ESPController/src/main.cpp +++ b/ESPController/src/main.cpp @@ -1554,15 +1554,15 @@ void setup() mqttClient.setCredentials(mysettings.mqtt_username, mysettings.mqtt_password); } - //Ensure we service the cell modules every 6 or 10 seconds, depending on number of cells being serviced + //Ensure we service the cell modules every 5 or 10 seconds, depending on number of cells being serviced //slower stops the queues from overflowing when a lot of cells are being monitored - myTimer.attach((TotalNumberOfCells() <= maximum_cell_modules_per_packet) ? 6:10, timerEnqueueCallback); + myTimer.attach((TotalNumberOfCells() <= maximum_cell_modules_per_packet) ? 5:10, timerEnqueueCallback); //Process rules every 5 seconds myTimerRelay.attach(5, timerProcessRules); //We process the transmit queue every 1 second (this needs to be lower delay than the queue fills) - //and slower than it takes a single module to process a command (about 300ms) + //and slower than it takes a single module to process a command (about 200ms @ 2400baud) myTransmitTimer.attach(1, timerTransmitCallback); //Service reply queue diff --git a/ESPController/web_src/default.htm b/ESPController/web_src/default.htm index f466ff2..c6057b0 100644 --- a/ESPController/web_src/default.htm +++ b/ESPController/web_src/default.htm @@ -114,6 +114,8 @@
Processor: %PLATFORM%
Version: %GIT_VERSION%
Compiled: %COMPILE_DATE_TIME%
+Free block size:
+Free heap: