From 868d153fa4525e7e3ba061ed239853ab12cebd59 Mon Sep 17 00:00:00 2001 From: Max Prokhorov Date: Tue, 29 Jan 2019 15:34:52 +0300 Subject: [PATCH 1/7] Compact WS data * send all ws config at once on start * reduce ws json overhead by creating message buffer manually * use k:[values] instead of k1:value1, k2:value2 etc. for lists --- code/espurna/config/prototypes.h | 4 + code/espurna/debug.ino | 49 ++++---- code/espurna/domoticz.ino | 15 +-- code/espurna/light.ino | 31 +++--- code/espurna/ntp.ino | 1 - code/espurna/relay.ino | 79 +++++++------ code/espurna/rfbridge.ino | 22 ++-- code/espurna/scheduler.ino | 51 ++++++--- code/espurna/sensor.ino | 59 ++++++++-- code/espurna/thinkspeak.ino | 11 +- code/espurna/ws.ino | 184 ++++++++++++++++++++----------- code/html/custom.js | 96 +++++++++------- 12 files changed, 367 insertions(+), 235 deletions(-) diff --git a/code/espurna/config/prototypes.h b/code/espurna/config/prototypes.h index d029ac46..b8c01ec3 100644 --- a/code/espurna/config/prototypes.h +++ b/code/espurna/config/prototypes.h @@ -184,6 +184,8 @@ void webRequestRegister(web_request_callback_f callback); #if WEB_SUPPORT typedef std::function ws_on_send_callback_f; void wsOnSendRegister(ws_on_send_callback_f callback); + void wsSend(uint32_t, JsonObject& root); + void wsSend(JsonObject& root); void wsSend(ws_on_send_callback_f sender); typedef std::function ws_on_action_callback_f; @@ -191,6 +193,8 @@ void webRequestRegister(web_request_callback_f callback); typedef std::function ws_on_receive_callback_f; void wsOnReceiveRegister(ws_on_receive_callback_f callback); + + bool wsDebugSend(const char*); #else #define ws_on_send_callback_f void * #define ws_on_action_callback_f void * diff --git a/code/espurna/debug.ino b/code/espurna/debug.ino index b4cd80a2..77e09d8a 100644 --- a/code/espurna/debug.ino +++ b/code/espurna/debug.ino @@ -8,6 +8,8 @@ Copyright (C) 2016-2018 by Xose Pérez #if DEBUG_SUPPORT +constexpr const uint8_t TIMESTAMP_LENGTH = 10; + #if DEBUG_UDP_SUPPORT #include WiFiUDP _udp_debug; @@ -18,20 +20,30 @@ char _udp_syslog_header[40] = {0}; void _debugSend(char * message) { + size_t msg_len = strlen(message); bool pause = false; #if DEBUG_ADD_TIMESTAMP static bool add_timestamp = true; - char timestamp[10] = {0}; - if (add_timestamp) snprintf_P(timestamp, sizeof(timestamp), PSTR("[%06lu] "), millis() % 1000000); - add_timestamp = (message[strlen(message)-1] == 10) || (message[strlen(message)-1] == 13); + + size_t offset = 0; + char buffer[TIMESTAMP_LENGTH + msg_len]; + + if (add_timestamp) { + snprintf_P(buffer, TIMESTAMP_LENGTH, PSTR("[%06lu] "), millis() % 1000000); + offset = TIMESTAMP_LENGTH - 1; + } + + memcpy(buffer + offset, message, msg_len); + buffer[msg_len + offset] = '\0'; + + add_timestamp = (message[msg_len - 1] == 10) || (message[msg_len - 1] == 13); + #else + char* buffer = message; #endif #if DEBUG_SERIAL_SUPPORT - #if DEBUG_ADD_TIMESTAMP - DEBUG_PORT.printf(timestamp); - #endif - DEBUG_PORT.printf(message); + DEBUG_PORT.print(buffer); #endif #if DEBUG_UDP_SUPPORT @@ -51,31 +63,12 @@ void _debugSend(char * message) { #endif #if DEBUG_TELNET_SUPPORT - #if DEBUG_ADD_TIMESTAMP - _telnetWrite(timestamp, strlen(timestamp)); - #endif - _telnetWrite(message, strlen(message)); + _telnetWrite(buffer, strlen(buffer)); pause = true; #endif #if DEBUG_WEB_SUPPORT - if (wsConnected() && (getFreeHeap() > 10000)) { - DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(1) + strlen(message) + 17); - JsonObject &root = jsonBuffer.createObject(); - #if DEBUG_ADD_TIMESTAMP - char buffer[strlen(timestamp) + strlen(message) + 1]; - snprintf_P(buffer, sizeof(buffer), "%s%s", timestamp, message); - root.set("weblog", buffer); - #else - root.set("weblog", message); - #endif - String out; - root.printTo(out); - jsonBuffer.clear(); - - wsSend(out.c_str()); - pause = true; - } + wsDebugSend(buffer); #endif if (pause) optimistic_yield(100); diff --git a/code/espurna/domoticz.ino b/code/espurna/domoticz.ino index eb923ace..bfdd9d1f 100644 --- a/code/espurna/domoticz.ino +++ b/code/espurna/domoticz.ino @@ -171,7 +171,7 @@ bool _domoticzWebSocketOnReceive(const char * key, JsonVariant& value) { void _domoticzWebSocketOnSend(JsonObject& root) { - root["dczVisible"] = 1; + unsigned char visible = 0; root["dczEnabled"] = getSetting("dczEnabled", DOMOTICZ_ENABLED).toInt() == 1; root["dczTopicIn"] = getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC); root["dczTopicOut"] = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC); @@ -180,18 +180,15 @@ void _domoticzWebSocketOnSend(JsonObject& root) { for (unsigned char i=0; i 0); #if SENSOR_SUPPORT - JsonArray& list = root.createNestedArray("dczMagnitudes"); - for (byte i=0; i 0); #endif + root["dczVisible"] = visible; + } #endif // WEB_SUPPORT diff --git a/code/espurna/light.ino b/code/espurna/light.ino index 9ed554d2..16b72d33 100644 --- a/code/espurna/light.ino +++ b/code/espurna/light.ino @@ -669,7 +669,7 @@ void _lightComms(unsigned char mask) { // Report color to WS clients (using current brightness setting) #if WEB_SUPPORT - wsSend(_lightWebSocketOnSend); + wsSend(_lightWebSocketStatus); #endif // Report channels to local broker @@ -824,23 +824,13 @@ bool _lightWebSocketOnReceive(const char * key, JsonVariant& value) { return false; } -void _lightWebSocketOnSend(JsonObject& root) { - root["colorVisible"] = 1; - root["mqttGroupColor"] = getSetting("mqttGroupColor"); - root["useColor"] = _light_has_color; - root["useWhite"] = _light_use_white; - root["useGamma"] = _light_use_gamma; - root["useTransitions"] = _light_use_transitions; - root["lightTime"] = _light_transition_time; - root["useCSS"] = getSetting("useCSS", LIGHT_USE_CSS).toInt() == 1; - bool useRGB = getSetting("useRGB", LIGHT_USE_RGB).toInt() == 1; - root["useRGB"] = useRGB; +void _lightWebSocketStatus(JsonObject& root) { if (_light_has_color) { if (_light_use_cct) { root["useCCT"] = _light_use_cct; root["mireds"] = _light_mireds; } - if (useRGB) { + if (getSetting("useRGB", LIGHT_USE_RGB).toInt() == 1) { root["rgb"] = lightColor(true); } else { root["hsv"] = lightColor(false); @@ -850,7 +840,20 @@ void _lightWebSocketOnSend(JsonObject& root) { for (unsigned char id=0; id < _light_channel.size(); id++) { channels.add(lightChannel(id)); } - root["brightness"] = lightBrightness(); +} + +void _lightWebSocketOnSend(JsonObject& root) { + root["colorVisible"] = 1; + root["mqttGroupColor"] = getSetting("mqttGroupColor"); + root["useColor"] = _light_has_color; + root["useWhite"] = _light_use_white; + root["useGamma"] = _light_use_gamma; + root["useTransitions"] = _light_use_transitions; + root["useCSS"] = getSetting("useCSS", LIGHT_USE_CSS).toInt() == 1; + root["useRGB"] = getSetting("useRGB", LIGHT_USE_RGB).toInt() == 1; + root["lightTime"] = _light_transition_time; + + _lightWebSocketStatus(root); } void _lightWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) { diff --git a/code/espurna/ntp.ino b/code/espurna/ntp.ino index 555017c5..2ffd41d9 100644 --- a/code/espurna/ntp.ino +++ b/code/espurna/ntp.ino @@ -34,7 +34,6 @@ void _ntpWebSocketOnSend(JsonObject& root) { root["ntpOffset"] = getSetting("ntpOffset", NTP_TIME_OFFSET).toInt(); root["ntpDST"] = getSetting("ntpDST", NTP_DAY_LIGHT).toInt() == 1; root["ntpRegion"] = getSetting("ntpRegion", NTP_DST_REGION).toInt(); - if (ntpSynced()) root["now"] = now(); } #endif diff --git a/code/espurna/relay.ino b/code/espurna/relay.ino index 87514dfc..22faef92 100644 --- a/code/espurna/relay.ino +++ b/code/espurna/relay.ino @@ -581,65 +581,78 @@ bool _relayWebSocketOnReceive(const char * key, JsonVariant& value) { void _relayWebSocketUpdate(JsonObject& root) { JsonArray& relay = root.createNestedArray("relayStatus"); for (unsigned char i=0; i(_relays[i].target_status); } } -void _relayWebSocketSendRelay(unsigned char i) { +String _relayFriendlyName(unsigned char i) { + String res = String("GPIO") + String(_relays[i].pin); - DynamicJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - JsonArray& config = root.createNestedArray("relayConfig"); - JsonObject& line = config.createNestedObject(); - - line["id"] = i; if (GPIO_NONE == _relays[i].pin) { #if (RELAY_PROVIDER == RELAY_PROVIDER_LIGHT) uint8_t physical = _relays.size() - DUMMY_RELAY_COUNT; if (i >= physical) { if (DUMMY_RELAY_COUNT == lightChannels()) { - line["gpio"] = String("CH") + String(i-physical); + res = String("CH") + String(i-physical); } else if (DUMMY_RELAY_COUNT == (lightChannels() + 1u)) { if (physical == i) { - line["gpio"] = String("Light"); + res = String("Light"); } else { - line["gpio"] = String("CH") + String(i-1-physical); + res = String("CH") + String(i-1-physical); } } else { - line["gpio"] = String("Light"); + res = String("Light"); } } else { - line["gpio"] = String("?"); + res = String("?"); } #else - line["gpio"] = String("SW") + String(i); + res = String("SW") + String(i); #endif - } else { - line["gpio"] = String("GPIO") + String(_relays[i].pin); } - - line["type"] = _relays[i].type; - line["reset"] = _relays[i].reset_pin; - line["boot"] = getSetting("relayBoot", i, RELAY_BOOT_MODE).toInt(); - line["pulse"] = _relays[i].pulse; - line["pulse_ms"] = _relays[i].pulse_ms / 1000.0; - #if MQTT_SUPPORT - line["group"] = getSetting("mqttGroup", i, ""); - line["group_inv"] = getSetting("mqttGroupInv", i, 0).toInt(); - line["on_disc"] = getSetting("relayOnDisc", i, 0).toInt(); - #endif - - String output; - root.printTo(output); - jsonBuffer.clear(); - wsSend((char *) output.c_str()); + return res; } void _relayWebSocketSendRelays() { + DynamicJsonBuffer jsonBuffer; + JsonObject& root = jsonBuffer.createObject(); + JsonObject& relays = root.createNestedObject("relayConfig"); + + relays["size"] = relayCount(); + relays["start"] = 0; + + JsonArray& gpio = relays.createNestedArray("gpio"); + JsonArray& type = relays.createNestedArray("type"); + JsonArray& reset = relays.createNestedArray("reset"); + JsonArray& boot = relays.createNestedArray("boot"); + JsonArray& pulse = relays.createNestedArray("pulse"); + JsonArray& pulse_time = relays.createNestedArray("pulse_time"); + + #if MQTT_SUPPORT + JsonArray& group = relays.createNestedArray("group"); + JsonArray& group_inverse = relays.createNestedArray("group_inv"); + JsonArray& on_disconnect = relays.createNestedArray("on_disc"); + #endif + for (unsigned char i=0; i 0) { - - root["schVisible"] = 1; - root["maxSchedules"] = SCHEDULER_MAX_SCHEDULES; - JsonArray &sch = root.createNestedArray("schedule"); - for (byte i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) { - if (!hasSetting("schSwitch", i)) break; - JsonObject &scheduler = sch.createNestedObject(); - scheduler["schEnabled"] = getSetting("schEnabled", i, 1).toInt() == 1; - scheduler["schSwitch"] = getSetting("schSwitch", i, 0).toInt(); - scheduler["schAction"] = getSetting("schAction", i, 0).toInt(); - scheduler["schType"] = getSetting("schType", i, 0).toInt(); - scheduler["schHour"] = getSetting("schHour", i, 0).toInt(); - scheduler["schMinute"] = getSetting("schMinute", i, 0).toInt(); - scheduler["schUTC"] = getSetting("schUTC", i, 0).toInt() == 1; - scheduler["schWDs"] = getSetting("schWDs", i, ""); - } - + if (!relayCount()) return; + + root["schVisible"] = 1; + root["maxSchedules"] = SCHEDULER_MAX_SCHEDULES; + + JsonObject &schedules = root.createNestedObject("schedules"); + uint8_t size = 0; + + JsonArray& enabled = schedules.createNestedArray("schEnabled"); + JsonArray& switch_ = schedules.createNestedArray("schSwitch"); + JsonArray& action = schedules.createNestedArray("schAction"); + JsonArray& type = schedules.createNestedArray("schType"); + JsonArray& hour = schedules.createNestedArray("schHour"); + JsonArray& minute = schedules.createNestedArray("schMinute"); + JsonArray& utc = schedules.createNestedArray("schUTC"); + JsonArray& weekdays = schedules.createNestedArray("schWDs"); + + for (byte i = 0; i < SCHEDULER_MAX_SCHEDULES; i++) { + if (!hasSetting("schSwitch", i)) break; + ++size; + + enabled.add(getSetting("schEnabled", i, 1).toInt() == 1); + utc.add(getSetting("schUTC", i, 0).toInt() == 1); + + switch_.add(getSetting("schSwitch", i, 0).toInt()); + action.add(getSetting("schAction", i, 0).toInt()); + type.add(getSetting("schType", i, 0).toInt()); + hour.add(getSetting("schHour", i, 0).toInt()); + minute.add(getSetting("schMinute", i, 0).toInt()); + weekdays.add(getSetting("schWDs", i, "")); } + schedules["size"] = size; + schedules["start"] = 0; + } #endif // WEB_SUPPORT diff --git a/code/espurna/sensor.ino b/code/espurna/sensor.ino index 4dc84e7b..a67d9c62 100644 --- a/code/espurna/sensor.ino +++ b/code/espurna/sensor.ino @@ -102,6 +102,32 @@ double _magnitudeProcess(unsigned char type, double value) { #if WEB_SUPPORT +template +void _sensorWebSocketMagnitudes(JsonObject& root, T prefix) { + + // ws produces flat list Magnitudes + String ws_name = String(prefix); + ws_name.concat("Magnitudes"); + + // config uses Magnitude (cut 's') + String conf_name = ws_name.substring(0, ws_name.length() - 1); + + JsonObject& list = root.createNestedObject(ws_name); + list["size"] = magnitudeCount(); + + JsonArray& name = list.createNestedArray("name"); + JsonArray& type = list.createNestedArray("type"); + JsonArray& index = list.createNestedArray("index"); + JsonArray& idx = list.createNestedArray("idx"); + + for (unsigned char i=0; ierror(); + index.add(magnitude.global); + type.add(magnitude.type); + value.add(buffer); + units.add(magnitudeUnits(magnitude.type)); + error.add(magnitude.sensor->error()); if (magnitude.type == MAGNITUDE_ENERGY) { if (_sensor_energy_reset_ts.length() == 0) _sensorResetTS(); - element["description"] = magnitude.sensor->slot(magnitude.local) + String(" (since ") + _sensor_energy_reset_ts + String(")"); + description.add(magnitude.sensor->slot(magnitude.local) + String(" (since ") + _sensor_energy_reset_ts + String(")")); } else { - element["description"] = magnitude.sensor->slot(magnitude.local); + description.add(magnitude.sensor->slot(magnitude.local)); } if (magnitude.type == MAGNITUDE_TEMPERATURE) hasTemperature = true; @@ -148,6 +183,8 @@ void _sensorWebSocketSendData(JsonObject& root) { #endif } + magnitudes["size"] = size; + if (hasTemperature) root["temperatureVisible"] = 1; if (hasHumidity) root["humidityVisible"] = 1; if (hasMICS) root["micsVisible"] = 1; @@ -210,7 +247,7 @@ void _sensorWebSocketStart(JsonObject& root) { } - if (_magnitudes.size() > 0) { + if (magnitudeCount()) { root["snsVisible"] = 1; //root["apiRealTime"] = _sensor_realtime; root["pwrUnits"] = _sensor_power_units; diff --git a/code/espurna/thinkspeak.ino b/code/espurna/thinkspeak.ino index 306d7666..db789e90 100644 --- a/code/espurna/thinkspeak.ino +++ b/code/espurna/thinkspeak.ino @@ -75,15 +75,8 @@ void _tspkWebSocketOnSend(JsonObject& root) { if (relayCount() > 0) visible = 1; #if SENSOR_SUPPORT - JsonArray& list = root.createNestedArray("tspkMagnitudes"); - for (byte i=0; i 0) visible = 1; + _sensorWebSocketMagnitudes(root, "tspk"); + visible = visible || (magnitudeCount() > 0); #endif root["tspkVisible"] = visible; diff --git a/code/espurna/ws.ino b/code/espurna/ws.ino index aa329e38..39f1b865 100644 --- a/code/espurna/ws.ino +++ b/code/espurna/ws.ino @@ -73,6 +73,22 @@ bool _wsAuth(AsyncWebSocketClient * client) { } +#if DEBUG_WEB_SUPPORT + +bool wsDebugSend(const char* message) { + if (!wsConnected()) return false; + if (getFreeHeap() < (strlen(message) * 3)) return false; + + DynamicJsonBuffer jsonBuffer; + JsonObject &root = jsonBuffer.createObject(); + root.set("weblog", message); + + wsSend(root); + + return true; +} +#endif + // ----------------------------------------------------------------------------- #if MQTT_SUPPORT @@ -289,6 +305,20 @@ void _wsUpdate(JsonObject& root) { #endif } +void _wsDoUpdate(bool reset = false) { + static unsigned long last = 0; + if (reset) { + last = 0; + return; + } + + if (millis() - last > WS_UPDATE_INTERVAL) { + last = millis(); + wsSend(_wsUpdate); + } +} + + bool _wsOnReceive(const char * key, JsonVariant& value) { if (strncmp(key, "ws", 2) == 0) return true; if (strncmp(key, "admin", 5) == 0) return true; @@ -298,69 +328,94 @@ bool _wsOnReceive(const char * key, JsonVariant& value) { } void _wsOnStart(JsonObject& root) { + char chipid[7]; + snprintf_P(chipid, sizeof(chipid), PSTR("%06X"), ESP.getChipId()); + uint8_t * bssid = WiFi.BSSID(); + char bssid_str[20]; + snprintf_P(bssid_str, sizeof(bssid_str), + PSTR("%02X:%02X:%02X:%02X:%02X:%02X"), + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5] + ); + + root["webMode"] = WEB_MODE_NORMAL; + + root["app_name"] = APP_NAME; + root["app_version"] = APP_VERSION; + root["app_build"] = buildTime(); + #if defined(APP_REVISION) + root["app_revision"] = APP_REVISION; + #endif + root["manufacturer"] = MANUFACTURER; + root["chipid"] = String(chipid); + root["mac"] = WiFi.macAddress(); + root["bssid"] = String(bssid_str); + root["channel"] = WiFi.channel(); + root["device"] = DEVICE; + root["hostname"] = getSetting("hostname"); + root["network"] = getNetwork(); + root["deviceip"] = getIP(); + root["sketch_size"] = ESP.getSketchSize(); + root["free_size"] = ESP.getFreeSketchSpace(); + root["sdk"] = ESP.getSdkVersion(); + root["core"] = getCoreVersion(); + + root["btnDelay"] = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt(); + root["webPort"] = getSetting("webPort", WEB_PORT).toInt(); + root["wsAuth"] = getSetting("wsAuth", WS_AUTHENTICATION).toInt() == 1; + #if TERMINAL_SUPPORT + root["cmdVisible"] = 1; + #endif + root["hbMode"] = getSetting("hbMode", HEARTBEAT_MODE).toInt(); + root["hbInterval"] = getSetting("hbInterval", HEARTBEAT_INTERVAL).toInt(); + + _wsDoUpdate(true); + +} + +void wsSend(JsonObject& root) { + size_t len = root.measureLength(); + AsyncWebSocketMessageBuffer* buffer = _ws.makeBuffer(len); + + if (buffer) { + root.printTo(reinterpret_cast(buffer->get()), len + 1); + _ws.textAll(buffer); + } +} + +void wsSend(uint32_t client_id, JsonObject& root) { + AsyncWebSocketClient* client = _ws.client(client_id); + if (client == nullptr) return; + size_t len = root.measureLength(); + AsyncWebSocketMessageBuffer* buffer = _ws.makeBuffer(len); + + if (buffer) { + root.printTo(reinterpret_cast(buffer->get()), len + 1); + client->text(buffer); + } +} + +void _wsStart(uint32_t client_id) { #if USE_PASSWORD && WEB_FORCE_PASS_CHANGE bool changePassword = getAdminPass().equals(ADMIN_PASS); #else bool changePassword = false; #endif - if (changePassword) { + DynamicJsonBuffer jsonBuffer; + JsonObject& root = jsonBuffer.createObject(); + if (changePassword) { root["webMode"] = WEB_MODE_PASSWORD; - - } else { - - char chipid[7]; - snprintf_P(chipid, sizeof(chipid), PSTR("%06X"), ESP.getChipId()); - uint8_t * bssid = WiFi.BSSID(); - char bssid_str[20]; - snprintf_P(bssid_str, sizeof(bssid_str), - PSTR("%02X:%02X:%02X:%02X:%02X:%02X"), - bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5] - ); - - root["webMode"] = WEB_MODE_NORMAL; - - root["app_name"] = APP_NAME; - root["app_version"] = APP_VERSION; - root["app_build"] = buildTime(); - #if defined(APP_REVISION) - root["app_revision"] = APP_REVISION; - #endif - root["manufacturer"] = MANUFACTURER; - root["chipid"] = String(chipid); - root["mac"] = WiFi.macAddress(); - root["bssid"] = String(bssid_str); - root["channel"] = WiFi.channel(); - root["device"] = DEVICE; - root["hostname"] = getSetting("hostname"); - root["network"] = getNetwork(); - root["deviceip"] = getIP(); - root["sketch_size"] = ESP.getSketchSize(); - root["free_size"] = ESP.getFreeSketchSpace(); - root["sdk"] = ESP.getSdkVersion(); - root["core"] = getCoreVersion(); - - _wsUpdate(root); - - root["btnDelay"] = getSetting("btnDelay", BUTTON_DBLCLICK_DELAY).toInt(); - root["webPort"] = getSetting("webPort", WEB_PORT).toInt(); - root["wsAuth"] = getSetting("wsAuth", WS_AUTHENTICATION).toInt() == 1; - #if TERMINAL_SUPPORT - root["cmdVisible"] = 1; - #endif - root["hbMode"] = getSetting("hbMode", HEARTBEAT_MODE).toInt(); - root["hbInterval"] = getSetting("hbInterval", HEARTBEAT_INTERVAL).toInt(); - + wsSend(root); + return; } -} - -void _wsStart(uint32_t client_id) { - for (unsigned char i = 0; i < _ws_on_send_callbacks.size(); i++) { - wsSend(client_id, _ws_on_send_callbacks[i]); + for (auto callback : _ws_on_send_callbacks) { + callback(root); } + + wsSend(client_id, root); } void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ @@ -408,12 +463,8 @@ void _wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTy } void _wsLoop() { - static unsigned long last = 0; if (!wsConnected()) return; - if (millis() - last > WS_UPDATE_INTERVAL) { - last = millis(); - wsSend(_wsUpdate); - } + _wsDoUpdate(); } // ----------------------------------------------------------------------------- @@ -441,10 +492,8 @@ void wsSend(ws_on_send_callback_f callback) { DynamicJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.createObject(); callback(root); - String output; - root.printTo(output); - jsonBuffer.clear(); - _ws.textAll((char *) output.c_str()); + + wsSend(root); } } @@ -463,13 +512,20 @@ void wsSend_P(PGM_P payload) { } void wsSend(uint32_t client_id, ws_on_send_callback_f callback) { + AsyncWebSocketClient* client = _ws.client(client_id); + if (client == nullptr) return; + DynamicJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.createObject(); callback(root); - String output; - root.printTo(output); - jsonBuffer.clear(); - _ws.text(client_id, (char *) output.c_str()); + + size_t len = root.measureLength(); + AsyncWebSocketMessageBuffer* buffer = _ws.makeBuffer(len); + + if (buffer) { + root.printTo(reinterpret_cast(buffer->get()), len + 1); + client->text(buffer); + } } void wsSend(uint32_t client_id, const char * payload) { diff --git a/code/html/custom.js b/code/html/custom.js index 1f2c8df7..94ce77b7 100644 --- a/code/html/custom.js +++ b/code/html/custom.js @@ -767,12 +767,13 @@ function createMagnitudeList(data, container, template_name) { if (current > 0) { return; } var template = $("#" + template_name + " .pure-g")[0]; - for (var i in data) { - var magnitude = data[i]; + var size = data.size; + + for (var i=0; i div").length / 6; // there are 6 divs per each relay + var current = $("#relayConfig > legend").length; // there is a legend per relay + if (current > 0) { return; } + + var size = data.size; + var start = data.start; + var template = $("#relayConfigTemplate").children(); - for (var i in data) { - - var relay = data[i]; - if (current > relay.id) continue; + for (var i=start; i function initMagnitudes(data) { - // check if already initialized + // check if already initialized (each magnitude is inside div.pure-g) var done = $("#magnitudes > div").length; if (done > 0) { return; } + var size = data.size; + // add templates var template = $("#magnitudeTemplate").children(); - for (var i in data) { - var magnitude = data[i]; + + for (var i=0; i @@ -1321,15 +1340,13 @@ function processData(data) { if ("magnitudes" === key) { initMagnitudes(value); - for (i in value) { - var magnitude = value[i]; - var error = magnitude.error || 0; + for (var i=0; i Date: Tue, 29 Jan 2019 16:33:53 +0300 Subject: [PATCH 2/7] fixup rfbridge ws-data --- code/espurna/rfbridge.ino | 7 ++++--- code/html/custom.js | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/code/espurna/rfbridge.ino b/code/espurna/rfbridge.ino index 7f2f0532..6060aca7 100644 --- a/code/espurna/rfbridge.ino +++ b/code/espurna/rfbridge.ino @@ -92,10 +92,11 @@ void _rfbWebSocketSendCodes() { DynamicJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.createObject(); - root["size"] = relayCount(); + JsonObject& rfb = root.createObject("rfb"); + rfb["size"] = relayCount(); - JsonArray& on = root.createNestedArray("on"); - JsonArray& off = root.createNestedArray("off"); + JsonArray& on = rfb.createNestedArray("on"); + JsonArray& off = rfb.createNestedArray("off"); for (byte id=0; id Date: Tue, 29 Jan 2019 16:49:49 +0300 Subject: [PATCH 3/7] partial sending of rfbridge codes --- code/espurna/rfbridge.ino | 19 ++++++++++++++----- code/html/custom.js | 3 ++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/code/espurna/rfbridge.ino b/code/espurna/rfbridge.ino index 6060aca7..fa8ff560 100644 --- a/code/espurna/rfbridge.ino +++ b/code/espurna/rfbridge.ino @@ -88,17 +88,18 @@ static bool _rfbToChar(byte * in, char * out, int n = RF_MESSAGE_SIZE) { #if WEB_SUPPORT -void _rfbWebSocketSendCodes() { +void _rfbWebSocketSendCodeArray(unsigned char start, unsigned char size) { DynamicJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.createObject(); - JsonObject& rfb = root.createObject("rfb"); - rfb["size"] = relayCount(); + JsonObject& rfb = root.createNestedObject("rfb"); + rfb["size"] = size; + rfb["start"] = start; JsonArray& on = rfb.createNestedArray("on"); JsonArray& off = rfb.createNestedArray("off"); - for (byte id=0; id Date: Thu, 7 Feb 2019 17:12:15 +0300 Subject: [PATCH 4/7] avoid copying callback std::function --- code/espurna/ws.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/espurna/ws.ino b/code/espurna/ws.ino index 39f1b865..4ed0a899 100644 --- a/code/espurna/ws.ino +++ b/code/espurna/ws.ino @@ -411,7 +411,7 @@ void _wsStart(uint32_t client_id) { return; } - for (auto callback : _ws_on_send_callbacks) { + for (auto& callback : _ws_on_send_callbacks) { callback(root); } From 4c6116a8f8c2055380486582d73bff68d5869380 Mon Sep 17 00:00:00 2001 From: Max Prokhorov Date: Thu, 7 Feb 2019 17:27:35 +0300 Subject: [PATCH 5/7] hide timestamp consts under ifdef --- code/espurna/debug.ino | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/code/espurna/debug.ino b/code/espurna/debug.ino index 77e09d8a..d8937f70 100644 --- a/code/espurna/debug.ino +++ b/code/espurna/debug.ino @@ -8,8 +8,6 @@ Copyright (C) 2016-2018 by Xose Pérez #if DEBUG_SUPPORT -constexpr const uint8_t TIMESTAMP_LENGTH = 10; - #if DEBUG_UDP_SUPPORT #include WiFiUDP _udp_debug; @@ -24,14 +22,17 @@ void _debugSend(char * message) { bool pause = false; #if DEBUG_ADD_TIMESTAMP + const char TIMESTAMP_FMT[] = "[%06lu] "; + const uint8_t TIMESTAMP_SIZE = 10; + static bool add_timestamp = true; size_t offset = 0; - char buffer[TIMESTAMP_LENGTH + msg_len]; + char buffer[TIMESTAMP_SIZE + msg_len]; if (add_timestamp) { - snprintf_P(buffer, TIMESTAMP_LENGTH, PSTR("[%06lu] "), millis() % 1000000); - offset = TIMESTAMP_LENGTH - 1; + snprintf(buffer, TIMESTAMP_SIZE, TIMESTAMP_FMT, millis() % 1000000); + offset = TIMESTAMP_SIZE - 1; } memcpy(buffer + offset, message, msg_len); From b2ad29a660e191bed58b1eef98b17373cab1d8c6 Mon Sep 17 00:00:00 2001 From: Max Prokhorov Date: Fri, 8 Feb 2019 00:48:38 +0300 Subject: [PATCH 6/7] Revert timestamp prepending, separate debug destinations * Simply print timestamp for supported destinations * Update ws destination to support 'prefix' sub-key * Avoid void casting in telnet module --- code/espurna/debug.ino | 49 ++++++++++++++++++++++++----------------- code/espurna/telnet.ino | 12 ++++++---- code/espurna/ws.ino | 9 ++++++-- code/html/custom.js | 7 +++++- 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/code/espurna/debug.ino b/code/espurna/debug.ino index d8937f70..2969d0ee 100644 --- a/code/espurna/debug.ino +++ b/code/espurna/debug.ino @@ -16,35 +16,43 @@ char _udp_syslog_header[40] = {0}; #endif #endif -void _debugSend(char * message) { +#if DEBUG_SERIAL_SUPPORT + void _debugSendSerial(const char* prefix, const char* data) { + if (prefix && (prefix[0] != '\0')) { + Serial.print(prefix); + } + Serial.print(data); - size_t msg_len = strlen(message); - bool pause = false; + } +#endif - #if DEBUG_ADD_TIMESTAMP - const char TIMESTAMP_FMT[] = "[%06lu] "; - const uint8_t TIMESTAMP_SIZE = 10; +#if DEBUG_TELNET_SUPPORT + void _debugSendTelnet(const char* prefix, const char* data) { + if (prefix && (prefix[0] != '\0')) { + _telnetWrite(prefix); + } + _telnetWrite(data); - static bool add_timestamp = true; + } +#endif - size_t offset = 0; - char buffer[TIMESTAMP_SIZE + msg_len]; +void _debugSend(const char * message) { - if (add_timestamp) { - snprintf(buffer, TIMESTAMP_SIZE, TIMESTAMP_FMT, millis() % 1000000); - offset = TIMESTAMP_SIZE - 1; - } + const size_t msg_len = strlen(message); - memcpy(buffer + offset, message, msg_len); - buffer[msg_len + offset] = '\0'; + bool pause = false; + char timestamp[10] = {0}; + #if DEBUG_ADD_TIMESTAMP + static bool add_timestamp = true; + if (add_timestamp) { + snprintf(timestamp, sizeof(timestamp), "[%06lu] ", millis() % 1000000); + } add_timestamp = (message[msg_len - 1] == 10) || (message[msg_len - 1] == 13); - #else - char* buffer = message; #endif #if DEBUG_SERIAL_SUPPORT - DEBUG_PORT.print(buffer); + _debugSendSerial(timestamp, message); #endif #if DEBUG_UDP_SUPPORT @@ -64,12 +72,13 @@ void _debugSend(char * message) { #endif #if DEBUG_TELNET_SUPPORT - _telnetWrite(buffer, strlen(buffer)); + _debugSendTelnet(timestamp, message); pause = true; #endif #if DEBUG_WEB_SUPPORT - wsDebugSend(buffer); + wsDebugSend(timestamp, message); + pause = true; #endif if (pause) optimistic_yield(100); diff --git a/code/espurna/telnet.ino b/code/espurna/telnet.ino index b4b53c7c..3108089f 100644 --- a/code/espurna/telnet.ino +++ b/code/espurna/telnet.ino @@ -45,14 +45,14 @@ void _telnetDisconnect(unsigned char clientId) { DEBUG_MSG_P(PSTR("[TELNET] Client #%d disconnected\n"), clientId); } -bool _telnetWrite(unsigned char clientId, void *data, size_t len) { +bool _telnetWrite(unsigned char clientId, const char *data, size_t len) { if (_telnetClients[clientId] && _telnetClients[clientId]->connected()) { - return (_telnetClients[clientId]->write((const char*) data, len) > 0); + return (_telnetClients[clientId]->write(data, len) > 0); } return false; } -unsigned char _telnetWrite(void *data, size_t len) { +unsigned char _telnetWrite(const char *data, size_t len) { unsigned char count = 0; for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) { // Do not send broadcast messages to unauthenticated clients @@ -65,8 +65,12 @@ unsigned char _telnetWrite(void *data, size_t len) { return count; } +unsigned char _telnetWrite(const char *data) { + return _telnetWrite(data, strlen(data)); +} + bool _telnetWrite(unsigned char clientId, const char * message) { - return _telnetWrite(clientId, (void *) message, strlen(message)); + return _telnetWrite(clientId, message, strlen(message)); } void _telnetData(unsigned char clientId, void *data, size_t len) { diff --git a/code/espurna/ws.ino b/code/espurna/ws.ino index 4ed0a899..89a9b20c 100644 --- a/code/espurna/ws.ino +++ b/code/espurna/ws.ino @@ -75,13 +75,18 @@ bool _wsAuth(AsyncWebSocketClient * client) { #if DEBUG_WEB_SUPPORT -bool wsDebugSend(const char* message) { +bool wsDebugSend(const char* prefix, const char* message) { if (!wsConnected()) return false; if (getFreeHeap() < (strlen(message) * 3)) return false; DynamicJsonBuffer jsonBuffer; JsonObject &root = jsonBuffer.createObject(); - root.set("weblog", message); + JsonObject &weblog = root.createNestedObject("weblog"); + + weblog.set("message", message); + if (prefix && (prefix[0] != '\0')) { + weblog.set("prefix", prefix); + } wsSend(root); diff --git a/code/html/custom.js b/code/html/custom.js index 810be461..fd194603 100644 --- a/code/html/custom.js +++ b/code/html/custom.js @@ -1478,7 +1478,12 @@ function processData(data) { // Web log if ("weblog" === key) { websock.send("{}"); - $("#weblog").append(new Text(value)); + + if (value.prefix) { + $("#weblog").append(new Text(value.prefix)); + } + $("#weblog").append(new Text(value.message)); + $("#weblog").scrollTop($("#weblog")[0].scrollHeight - $("#weblog").height()); return; } From ee19446aaf2a119bc23fb90a13adf2bb084df67c Mon Sep 17 00:00:00 2001 From: Max Prokhorov Date: Fri, 8 Feb 2019 01:27:47 +0300 Subject: [PATCH 7/7] fix wsDebugSend prototype --- code/espurna/config/prototypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/espurna/config/prototypes.h b/code/espurna/config/prototypes.h index b8c01ec3..26a146db 100644 --- a/code/espurna/config/prototypes.h +++ b/code/espurna/config/prototypes.h @@ -194,7 +194,7 @@ void webRequestRegister(web_request_callback_f callback); typedef std::function ws_on_receive_callback_f; void wsOnReceiveRegister(ws_on_receive_callback_f callback); - bool wsDebugSend(const char*); + bool wsDebugSend(const char*, const char*); #else #define ws_on_send_callback_f void * #define ws_on_action_callback_f void *