diff --git a/README.md b/README.md index a5a018646..8c8d7c024 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ People helping to keep the show on the road: - Christian Staars for NRF24L01 and HM-10 Bluetooth sensor support - Paul Diem for UDP Group communication support - Jörg Schüler-Maroldt for his initial ESP32 port +- Javier Arigita for his thermostat driver - Many more providing Tips, Wips, Pocs, PRs and Donations ## License diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.cpp b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.cpp index 686b0391e..90e257483 100644 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.cpp +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.cpp @@ -364,8 +364,12 @@ const unsigned char lut_partial_update[] = #define PIN_OUT_SET 0x60000304 #define PIN_OUT_CLEAR 0x60000308 -#define PWRITE xdigitalWrite - +#ifdef ESP32 +#define SSPI_USEANYPIN 1 +#define PWRITE digitalWrite +#else +#define PWRITE ydigitalWrite +#endif #ifndef SSPI_USEANYPIN // uses about 2.75 usecs, 365 kb /sec @@ -388,6 +392,7 @@ void ICACHE_RAM_ATTR Epd::fastSPIwrite(uint8_t d,uint8_t dc) { } #else +#ifndef ESP32 extern void ICACHE_RAM_ATTR xdigitalWrite(uint8_t pin, uint8_t val) { //stopWaveform(pin); if(pin < 16){ @@ -398,6 +403,7 @@ extern void ICACHE_RAM_ATTR xdigitalWrite(uint8_t pin, uint8_t val) { else GP16O &= ~1; } } +#endif // about 13 us => 76 kb / sec // can use any pin diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp index e2fe5dd34..8450a9647 100644 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp @@ -130,14 +130,14 @@ int Epd42::Init(void) { SendCommand(PANEL_SETTING); // SendData(0xbf); // KW-BF KWR-AF BWROTP 0f // SendData(0x0b); -// SendData(0x0F); //300x400 Red mode, LUT from OTP -// SendData(0x1F); //300x400 B/W mode, LUT from OTP - SendData(0x3F); //300x400 B/W mode, LUT set by register -// SendData(0x2F); //300x400 Red mode, LUT set by register +// SendData(0x0F); //300x400 Red mode, LUT from OTP +// SendData(0x1F); //300x400 B/W mode, LUT from OTP + SendData(0x3F); //300x400 B/W mode, LUT set by register +// SendData(0x2F); //300x400 Red mode, LUT set by register SendCommand(PLL_CONTROL); SendData(0x3C); // 3A 100Hz 29 150Hz 39 200Hz 31 171Hz 3C 50Hz (default) 0B 10Hz - //SendData(0x0B); //0B is 10Hz + //SendData(0x0B); //0B is 10Hz /* EPD hardware init end */ return 0; } @@ -502,12 +502,15 @@ const unsigned char lut_wb_quick[] PROGMEM = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - - #define PIN_OUT_SET 0x60000304 #define PIN_OUT_CLEAR 0x60000308 +#ifdef ESP32 +#define SSPI_USEANYPIN 1 +#define PWRITE digitalWrite +#else #define PWRITE ydigitalWrite +#endif #ifndef SSPI_USEANYPIN // uses about 2.75 usecs, 365 kb /sec @@ -530,6 +533,7 @@ void ICACHE_RAM_ATTR Epd42::fastSPIwrite(uint8_t d,uint8_t dc) { } #else +#ifndef ESP32 extern void ICACHE_RAM_ATTR ydigitalWrite(uint8_t pin, uint8_t val) { //stopWaveform(pin); if(pin < 16){ @@ -540,6 +544,7 @@ extern void ICACHE_RAM_ATTR ydigitalWrite(uint8_t pin, uint8_t val) { else GP16O &= ~1; } } +#endif // about 13 us => 76 kb / sec // can use any pin void Epd42::fastSPIwrite(uint8_t d,uint8_t dc) { diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 934e03c74..46f92021c 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -20,6 +20,7 @@ - Change default PWM Frequency to 977 Hz from 223 Hz - Change minimum PWM Frequency from 100 Hz to 40 Hz - Change PWM updated to the latest version of Arduino PR #7231 +- Change Philips Hue emulation now exposes modelId and manufacturerId ### 8.2.0.5 20200425 diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 60c4f87fb..2e3eb4a5f 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -675,13 +675,12 @@ #define THERMOSTAT_SENSOR_NAME "DS18B20" // Name of the local sensor to be used #define THERMOSTAT_RELAY_NUMBER 1 // Default output relay number for the first controller (+i for following ones) #define THERMOSTAT_SWITCH_NUMBER 1 // Default input switch number for the first controller (+i for following ones) - #define THERMOSTAT_TIME_ALLOW_RAMPUP 300 // Default time in seconds after last target update to allow ramp-up controller phase in minutes + #define THERMOSTAT_TIME_ALLOW_RAMPUP 300 // Default time after last target update to allow ramp-up controller phase in minutes #define THERMOSTAT_TIME_RAMPUP_MAX 960 // Default time maximum ramp-up controller duration in minutes - #define THERMOSTAT_TIME_RAMPUP_CYCLE 1800 // Default time ramp-up cycle in seconds + #define THERMOSTAT_TIME_RAMPUP_CYCLE 30 // Default time ramp-up cycle in minutes #define THERMOSTAT_TIME_SENS_LOST 30 // Maximum time w/o sensor update to set it as lost in minutes #define THERMOSTAT_TEMP_SENS_NUMBER 1 // Default temperature sensor number #define THERMOSTAT_TIME_MANUAL_TO_AUTO 60 // Default time without input switch active to change from manual to automatic in minutes - #define THERMOSTAT_TIME_ON_LIMIT 120 // Default maximum time with output active in minutes #define THERMOSTAT_TIME_RESET 12000 // Default reset time of the PI controller in seconds #define THERMOSTAT_TIME_PI_CYCLE 30 // Default cycle time for the thermostat controller in minutes #define THERMOSTAT_TIME_MAX_ACTION 20 // Default maximum thermostat time per cycle in minutes diff --git a/tasmota/support.ino b/tasmota/support.ino index 45c6b0e7a..db4c9c26e 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -638,7 +638,7 @@ float ConvertHumidity(float h) float CalcTempHumToDew(float t, float h) { - if (isnan(h) || isnan(t)) { return 0.0; } + if (isnan(h) || isnan(t)) { return NAN; } if (Settings.flag.temperature_conversion) { // SetOption8 - Switch between Celsius or Fahrenheit t = (t - 32) / 1.8; // Celsius @@ -1863,3 +1863,62 @@ void AddLogBufferSize(uint32_t loglevel, uint8_t *buffer, uint32_t count, uint32 } AddLog(loglevel); } + +/*********************************************************************************************\ + * JSON parsing +\*********************************************************************************************/ + +// does the character needs to be escaped, and if so with which character +char escapeJSONChar(char c) { + if ((c == '\"') || (c == '\\')) { + return c; + } + if (c == '\n') { return 'n'; } + if (c == '\t') { return 't'; } + if (c == '\r') { return 'r'; } + if (c == '\f') { return 'f'; } + if (c == '\b') { return 'b'; } + return 0; +} + +String escapeJSONString(const char *str) { + String r(""); + if (nullptr == str) { return r; } + + bool needs_escape = false; + size_t len_out = 1; + const char * c = str; + + while (*c) { + if (escapeJSONChar(*c)) { + len_out++; + needs_escape = true; + } + c++; + len_out++; + } + + if (needs_escape) { + // we need to escape some chars + // allocate target buffer + r.reserve(len_out); + c = str; + char *d = r.begin(); + while (*c) { + char c2 = escapeJSONChar(*c); + if (c2) { + c++; + *d++ = '\\'; + *d++ = c2; + } else { + *d++ = *c++; + } + } + *d = 0; // add NULL terminator + r = (char*) r.begin(); // assign the buffer to the string + } else { + r = str; + } + + return r; +} \ No newline at end of file diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 47aca2397..728d20f7f 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1227,6 +1227,17 @@ void SerialInput(void) delay(0); serial_in_byte = Serial.read(); + if (0 == serial_in_byte_counter) { + serial_buffer_overrun = false; + } + else if ((serial_in_byte_counter == INPUT_BUFFER_SIZE) +#ifdef ESP8266 +// || Serial.hasOverrun() // Default ESP8266 Serial buffer size is 256. Tasmota increases to INPUT_BUFFER_SIZE +#endif + ) { + serial_buffer_overrun = true; + } + #ifdef ESP8266 /*-------------------------------------------------------------------------------------------*\ * Sonoff dual and ch4 19200 baud serial interface @@ -1255,7 +1266,7 @@ void SerialInput(void) if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // Add char to string if it still fits serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; } else { - serial_in_byte_counter = 0; + serial_buffer_overrun = true; // Signal overrun but continue reading input to flush until '\n' (EOL) } } } else { @@ -1292,8 +1303,12 @@ void SerialInput(void) if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { // CMND_SERIALSEND and CMND_SERIALLOG serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (uint8_t)LOG_LEVEL_INFO : Settings.seriallog_level; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); - ExecuteCommand(serial_in_buffer, SRC_SERIAL); + if (serial_buffer_overrun) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "Serial buffer overrun")); + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); + ExecuteCommand(serial_in_buffer, SRC_SERIAL); + } serial_in_byte_counter = 0; serial_polling_window = 0; Serial.flush(); diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index ab152f9ec..3cb309910 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -124,7 +124,7 @@ const uint16_t SYSLOG_TIMER = 600; // Seconds to restore syslog_level const uint16_t SERIALLOG_TIMER = 600; // Seconds to disable SerialLog const uint8_t OTA_ATTEMPTS = 5; // Number of times to try fetching the new firmware -const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in (serial and http) command buffer +const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in serial command buffer const uint16_t FLOATSZ = 16; // Max number of characters in float result from dtostrfd (max 32) const uint16_t CMDSZ = 24; // Max number of characters in command const uint16_t TOPSZ = 151; // Max number of characters in topic string diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 6425d7fae..d1d841810 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -156,7 +156,8 @@ uint8_t last_source = 0; // Last command source uint8_t shutters_present = 0; // Number of actual define shutters uint8_t prepped_loglevel = 0; // Delayed log level message //uint8_t mdns_delayed_start = 0; // mDNS delayed start -bool serial_local = false; // Handle serial locally; +bool serial_local = false; // Handle serial locally +bool serial_buffer_overrun = false; // Serial buffer overrun bool fallback_topic_flag = false; // Use Topic or FallbackTopic bool backlog_mutex = false; // Command backlog pending bool interlock_mutex = false; // Interlock power command pending @@ -215,6 +216,7 @@ void setup(void) RtcRebootSave(); Serial.begin(APP_BAUDRATE); + Serial.setRxBufferSize(INPUT_BUFFER_SIZE); // Default is 256 chars seriallog_level = LOG_LEVEL_INFO; // Allow specific serial messages until config loaded snprintf_P(my_version, sizeof(my_version), PSTR("%d.%d.%d"), VERSION >> 24 & 0xff, VERSION >> 16 & 0xff, VERSION >> 8 & 0xff); // Release version 6.3.0 diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index 33f61a68c..922014e49 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -1992,12 +1992,21 @@ void CmndRule(void) } Rules.triggers[index -1] = 0; // Reset once flag } + String rule = GetRule(index - 1); + size_t rule_len = rule.length(); + if (rule_len >= MAX_RULE_SIZE) { + // we need to split the rule in chunks + rule = rule.substring(0, MAX_RULE_SIZE); + rule += F("..."); + } // snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"), // XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), // GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[index -1]) - strlen(Settings.rules[index -1]) -1, Settings.rules[index -1]); - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"), + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Length\":%d,\"Free\":%d,\"Rules\":\"%s\"}"), XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), - GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[0]) - GetRuleLenStorage(index - 1), GetRule(index - 1).c_str()); + GetStateText(bitRead(Settings.rule_stop, index -1)), + rule_len, MAX_RULE_SIZE - GetRuleLenStorage(index - 1), + escapeJSONString(rule.c_str()).c_str()); } } diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 38c8f158f..d16c0bd31 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -1879,6 +1879,19 @@ chknext: len=0; goto strexit; } +#ifdef ESP32 + if (!strncmp(vname,"sf(",3)) { + lp+=2; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + if (fvar<80) fvar=80; + if (fvar>240) fvar=240; + setCpuFrequencyMhz(fvar); + fvar=getCpuFrequencyMhz(); + lp++; + len=0; + goto exit; + } +#endif #if defined(USE_TIMERS) && defined(USE_SUNRISE) if (!strncmp(vname,"sunrise",7)) { fvar=SunMinutes(0); @@ -2093,7 +2106,7 @@ chknext: len=0; goto exit; } -#endif +#endif //ESP32, USE_WEBCAM if (!strncmp(vname,"wday",4)) { fvar=RtcTime.day_of_week; goto exit; @@ -2692,11 +2705,13 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { return -99; } - DynamicJsonBuffer jsonBuffer; // on heap - JsonObject &jobj=jsonBuffer.parseObject(js); - JsonObject *jo; - if (js) jo=&jobj; - else jo=0; + JsonObject *jo=0; + + if (js) { + DynamicJsonBuffer jsonBuffer; // on heap + JsonObject &jobj=jsonBuffer.parseObject(js); + jo=&jobj; + } char *lp=glob_script_mem.scriptptr; @@ -5119,6 +5134,8 @@ bool RulesProcessEvent(char *json_event) { #ifdef USE_SCRIPT_TASK uint16_t task_timer1; uint16_t task_timer2; +TaskHandle_t task_t1; +TaskHandle_t task_t2; void script_task1(void *arg) { while (1) { @@ -5143,14 +5160,16 @@ void script_task2(void *arg) { uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core) { //return 0; - BaseType_t res=0; - if (core>1) {core = 1;} + BaseType_t res = 0; + if (core > 1) { core = 1; } if (num == 1) { - res = xTaskCreatePinnedToCore(script_task1, "T 1", STASK_STACK, NULL, STASK_PRIO, NULL, core); - task_timer1=time; + if (task_t1) { vTaskDelete(task_t1); } + res = xTaskCreatePinnedToCore(script_task1, "T1", STASK_STACK, NULL, STASK_PRIO, &task_t1, core); + task_timer1 = time; } else { - res = xTaskCreatePinnedToCore(script_task2, "T 2", STASK_STACK, NULL, STASK_PRIO, NULL, core); - task_timer2=time; + if (task_t2) { vTaskDelete(task_t2); } + res = xTaskCreatePinnedToCore(script_task2, "T2", STASK_STACK, NULL, STASK_PRIO, &task_t2, core); + task_timer2 = time; } return res; } diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index 201491d09..122166010 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -144,9 +144,9 @@ const char HUE_LIGHTS_STATUS_JSON1_SUFFIX[] PROGMEM = const char HUE_LIGHTS_STATUS_JSON2[] PROGMEM = ",\"type\":\"Extended color light\"," "\"name\":\"%s\"," - "\"modelid\":\"LCT007\"," - "\"uniqueid\":\"%s\"," - "\"swversion\":\"5.50.1.19085\"}"; + "\"modelid\":\"%s\"," + "\"manufacturername\":\"%s\"," + "\"uniqueid\":\"%s\"}"; const char HUE_GROUP0_STATUS_JSON[] PROGMEM = "{\"name\":\"Group 0\"," "\"lights\":[{l1]," @@ -358,7 +358,7 @@ bool HueActive(uint8_t device) { void HueLightStatus2(uint8_t device, String *response) { - const size_t buf_size = 192; + const size_t buf_size = 300; char * buf = (char*) malloc(buf_size); const size_t max_name_len = 32; char fname[max_name_len + 1]; @@ -376,7 +376,11 @@ void HueLightStatus2(uint8_t device, String *response) } fname[fname_len] = 0x00; } - snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2, fname, GetHueDeviceId(device).c_str()); + snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2, + escapeJSONString(fname).c_str(), + escapeJSONString(Settings.user_template_name).c_str(), + PSTR("Tasmota"), + GetHueDeviceId(device).c_str()); *response += buf; free(buf); } diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index 23970042e..76413e23e 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -127,6 +127,7 @@ public: void setFriendlyName(uint16_t shortaddr, const char * str); const char * getFriendlyName(uint16_t shortaddr) const; const char * getModelId(uint16_t shortaddr) const; + const char * getManufacturerId(uint16_t shortaddr) const; void setReachable(uint16_t shortaddr, bool reachable); // get next sequence number for (increment at each all) @@ -589,6 +590,15 @@ const char * Z_Devices::getModelId(uint16_t shortaddr) const { return nullptr; } +const char * Z_Devices::getManufacturerId(uint16_t shortaddr) const { + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + const Z_Device & device = devicesAt(found); + return device.manufacturerId; + } + return nullptr; +} + void Z_Devices::setReachable(uint16_t shortaddr, bool reachable) { Z_Device & device = getShortAddr(shortaddr); if (&device == nullptr) { return; } // don't crash if not found diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino index 87b7d39da..d5c4b1f08 100644 --- a/tasmota/xdrv_23_zigbee_3_hue.ino +++ b/tasmota/xdrv_23_zigbee_3_hue.ino @@ -75,16 +75,21 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri void HueLightStatus2Zigbee(uint16_t shortaddr, String *response) { - const size_t buf_size = 192; + const size_t buf_size = 300; char * buf = (char*) malloc(buf_size); const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); + const char * modelId = zigbee_devices.getModelId(shortaddr); + const char * manufacturerId = zigbee_devices.getManufacturerId(shortaddr); char shortaddrname[8]; snprintf_P(shortaddrname, sizeof(shortaddrname), PSTR("0x%04X"), shortaddr); snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2, - (friendlyName) ? friendlyName : shortaddrname, + (friendlyName) ? escapeJSONString(friendlyName).c_str() : shortaddrname, + (modelId) ? escapeJSONString(modelId).c_str() : PSTR("Unknown"), + (manufacturerId) ? escapeJSONString(manufacturerId).c_str() : PSTR("Tasmota"), GetHueDeviceId(shortaddr).c_str()); + *response += buf; free(buf); } diff --git a/tasmota/xdrv_39_thermostat.ino b/tasmota/xdrv_39_thermostat.ino index 0ccf777ad..5301d9ca2 100644 --- a/tasmota/xdrv_39_thermostat.ino +++ b/tasmota/xdrv_39_thermostat.ino @@ -134,18 +134,18 @@ const char kThermostatCommands[] PROGMEM = "|" D_CMND_THERMOSTATMODESET "|" D_CM D_CMND_TEMPFROSTPROTECTSET "|" D_CMND_CONTROLLERMODESET "|" D_CMND_INPUTSWITCHSET "|" D_CMND_INPUTSWITCHUSE "|" D_CMND_OUTPUTRELAYSET "|" D_CMND_TIMEALLOWRAMPUPSET "|" D_CMND_TEMPFORMATSET "|" D_CMND_TEMPMEASUREDSET "|" D_CMND_TEMPTARGETSET "|" D_CMND_TEMPMEASUREDGRDREAD "|" D_CMND_SENSORINPUTSET "|" D_CMND_STATEEMERGENCYSET "|" - D_CMND_TIMEMANUALTOAUTOSET "|" D_CMND_TIMEONLIMITSET "|" D_CMND_PROPBANDSET "|" D_CMND_TIMERESETSET "|" - D_CMND_TIMEPICYCLESET "|" D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_TIMEMAXACTIONSET "|" - D_CMND_TIMEMINACTIONSET "|" D_CMND_TIMEMINTURNOFFACTIONSET "|" D_CMND_TEMPRUPDELTINSET "|" D_CMND_TEMPRUPDELTOUTSET "|" - D_CMND_TIMERAMPUPMAXSET "|" D_CMND_TIMERAMPUPCYCLESET "|" D_CMND_TEMPRAMPUPPIACCERRSET "|" D_CMND_TIMEPIPROPORTREAD "|" - D_CMND_TIMEPIINTEGRREAD "|" D_CMND_TIMESENSLOSTSET "|" D_CMND_DIAGNOSTICMODESET; + D_CMND_TIMEMANUALTOAUTOSET "|" D_CMND_PROPBANDSET "|" D_CMND_TIMERESETSET "|" D_CMND_TIMEPICYCLESET "|" + D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_TIMEMAXACTIONSET "|" D_CMND_TIMEMINACTIONSET "|" + D_CMND_TIMEMINTURNOFFACTIONSET "|" D_CMND_TEMPRUPDELTINSET "|" D_CMND_TEMPRUPDELTOUTSET "|" D_CMND_TIMERAMPUPMAXSET "|" + D_CMND_TIMERAMPUPCYCLESET "|" D_CMND_TEMPRAMPUPPIACCERRSET "|" D_CMND_TIMEPIPROPORTREAD "|" D_CMND_TIMEPIINTEGRREAD "|" + D_CMND_TIMESENSLOSTSET "|" D_CMND_DIAGNOSTICMODESET; void (* const ThermostatCommand[])(void) PROGMEM = { &CmndThermostatModeSet, &CmndClimateModeSet, &CmndTempFrostProtectSet, &CmndControllerModeSet, &CmndInputSwitchSet, &CmndInputSwitchUse, &CmndOutputRelaySet, &CmndTimeAllowRampupSet, &CmndTempFormatSet, &CmndTempMeasuredSet, &CmndTempTargetSet, &CmndTempMeasuredGrdRead, &CmndSensorInputSet, &CmndStateEmergencySet, &CmndTimeManualToAutoSet, - &CmndTimeOnLimitSet, &CmndPropBandSet, &CmndTimeResetSet, &CmndTimePiCycleSet, &CmndTempAntiWindupResetSet, - &CmndTempHystSet, &CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet, + &CmndPropBandSet, &CmndTimeResetSet, &CmndTimePiCycleSet, &CmndTempAntiWindupResetSet, &CmndTempHystSet, + &CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet, &CmndTempRupDeltOutSet, &CmndTimeRampupMaxSet, &CmndTimeRampupCycleSet, &CmndTempRampupPiAccErrSet, &CmndTimePiProportRead, &CmndTimePiIntegrRead, &CmndTimeSensLostSet, &CmndDiagnosticModeSet }; @@ -183,16 +183,15 @@ struct THERMOSTAT { int16_t temp_rampup_start = 0; // Temperature at start of ramp-up controller in tenths of degrees celsius int16_t temp_rampup_cycle = 0; // Temperature set at the beginning of each ramp-up cycle in tenths of degrees uint16_t time_rampup_max = THERMOSTAT_TIME_RAMPUP_MAX; // Time maximum ramp-up controller duration in minutes - uint16_t time_rampup_cycle = THERMOSTAT_TIME_RAMPUP_CYCLE; // Time ramp-up cycle in seconds + uint16_t time_rampup_cycle = THERMOSTAT_TIME_RAMPUP_CYCLE; // Time ramp-up cycle in minutes uint16_t time_allow_rampup = THERMOSTAT_TIME_ALLOW_RAMPUP; // Time in minutes after last target update to allow ramp-up controller phase - uint16_t time_sens_lost = THERMOSTAT_TIME_SENS_LOST; // Maximum time w/o sensor update to set it as lost + uint16_t time_sens_lost = THERMOSTAT_TIME_SENS_LOST; // Maximum time w/o sensor update to set it as lost in minutes uint16_t time_manual_to_auto = THERMOSTAT_TIME_MANUAL_TO_AUTO; // Time without input switch active to change from manual to automatic in minutes - uint16_t time_on_limit = THERMOSTAT_TIME_ON_LIMIT; // Maximum time with output active in minutes - uint16_t time_pi_cycle = THERMOSTAT_TIME_PI_CYCLE; // Cycle time for the thermostat controller in seconds uint32_t time_reset = THERMOSTAT_TIME_RESET; // Reset time of the PI controller in seconds + uint16_t time_pi_cycle = THERMOSTAT_TIME_PI_CYCLE; // Cycle time for the thermostat controller in minutes uint16_t time_max_action = THERMOSTAT_TIME_MAX_ACTION; // Maximum thermostat time per cycle in minutes uint16_t time_min_action = THERMOSTAT_TIME_MIN_ACTION; // Minimum thermostat time per cycle in minutes - uint16_t time_min_turnoff_action = THERMOSTAT_TIME_MIN_TURNOFF_ACTION; // Minimum turnoff time in minutes, below it the thermostat will be held on + uint16_t time_min_turnoff_action = THERMOSTAT_TIME_MIN_TURNOFF_ACTION; // Minimum turnoff time in minutes, below it the thermostat will stay on uint8_t temp_reset_anti_windup = THERMOSTAT_TEMP_RESET_ANTI_WINDUP; // Range where reset antiwindup is disabled, in tenths of degrees celsius int8_t temp_hysteresis = THERMOSTAT_TEMP_HYSTERESIS; // Range hysteresis for temperature PI controller, in tenths of degrees celsius uint8_t temp_frost_protect = THERMOSTAT_TEMP_FROST_PROTECT; // Minimum temperature for frost protection, in tenths of degrees celsius @@ -605,7 +604,7 @@ void ThermostatCalculatePI(uint8_t ctr_output) // Antiwindup of the integrator // If integral calculation is bigger than cycle time, adjust result - // to the cycle time and error will not be cummulated]] + // to the cycle time and error will not be cummulated if (Thermostat[ctr_output].time_integral_pi > ((uint32_t)Thermostat[ctr_output].time_pi_cycle * 60)) { Thermostat[ctr_output].time_integral_pi = ((uint32_t)Thermostat[ctr_output].time_pi_cycle * 60); } @@ -616,7 +615,7 @@ void ThermostatCalculatePI(uint8_t ctr_output) // Antiwindup of the output // If result is bigger than cycle time, the result will be adjusted - // to the cylce time minus safety time and error will not be cummulated]] + // to the cylce time minus safety time and error will not be cummulated if (Thermostat[ctr_output].time_total_pi >= ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60)) { // Limit to cycle time //at least switch down a minimum time Thermostat[ctr_output].time_total_pi = ((int32_t)Thermostat[ctr_output].time_pi_cycle * 60); @@ -651,13 +650,13 @@ void ThermostatCalculatePI(uint8_t ctr_output) } // Minimum action limiter - // If result is less than the minimum action time, adjust to minimum value]] + // If result is less than the minimum action time, adjust to minimum value if ((Thermostat[ctr_output].time_total_pi <= abs(((uint32_t)Thermostat[ctr_output].time_min_action * 60))) && (Thermostat[ctr_output].time_total_pi != 0)) { Thermostat[ctr_output].time_total_pi = ((int32_t)Thermostat[ctr_output].time_min_action * 60); } // Maximum action limiter - // If result is more than the maximum action time, adjust to maximum value]] + // If result is more than the maximum action time, adjust to maximum value else if (Thermostat[ctr_output].time_total_pi > abs(((int32_t)Thermostat[ctr_output].time_max_action * 60))) { Thermostat[ctr_output].time_total_pi = ((int32_t)Thermostat[ctr_output].time_max_action * 60); } @@ -747,7 +746,7 @@ void ThermostatWorkAutomaticRampUp(uint8_t ctr_output) } // Calculate absolute gradient since start of ramp-up (considering deadtime) in thousandths of º/hour Thermostat[ctr_output].temp_rampup_meas_gradient = (int32_t)((360000 * (int32_t)temp_delta_rampup) / (int32_t)time_in_rampup); - Thermostat[ctr_output].time_rampup_nextcycle = uptime + (uint32_t)Thermostat[ctr_output].time_rampup_cycle; + Thermostat[ctr_output].time_rampup_nextcycle = uptime + ((uint32_t)Thermostat[ctr_output].time_rampup_cycle * 60); // Set auxiliary variables Thermostat[ctr_output].temp_rampup_cycle = Thermostat[ctr_output].temp_measured; Thermostat[ctr_output].time_ctr_changepoint = uptime + (60 * (uint32_t)Thermostat[ctr_output].time_rampup_max); @@ -758,7 +757,7 @@ void ThermostatWorkAutomaticRampUp(uint8_t ctr_output) // Calculate temp. gradient in º/hour and set again time_rampup_nextcycle and temp_rampup_cycle // temp_rampup_meas_gradient = ((3600 * temp_delta_rampup) / (os.time() - time_rampup_nextcycle)) temp_delta_rampup = Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_rampup_cycle; - uint32_t time_total_rampup = (uint32_t)Thermostat[ctr_output].time_rampup_cycle * Thermostat[ctr_output].counter_rampup_cycles; + uint32_t time_total_rampup = (uint32_t)Thermostat[ctr_output].time_rampup_cycle * 60 * Thermostat[ctr_output].counter_rampup_cycles; // Translate into gradient per hour (thousandths of ° per hour) Thermostat[ctr_output].temp_rampup_meas_gradient = int32_t((360000 * (int32_t)temp_delta_rampup) / (int32_t)time_total_rampup); if ( ((Thermostat[ctr_output].temp_rampup_meas_gradient > 0) @@ -776,7 +775,7 @@ void ThermostatWorkAutomaticRampUp(uint8_t ctr_output) // y = (((y2-y1)/(x2-x1))*(x-x1)) + y1 Thermostat[ctr_output].temp_rampup_output_off = (int16_t)(((int32_t)temp_delta_rampup * (int32_t)(Thermostat[ctr_output].time_ctr_changepoint - (uptime - (time_total_rampup)))) / (int32_t)(time_total_rampup * Thermostat[ctr_output].counter_rampup_cycles)) + Thermostat[ctr_output].temp_rampup_cycle; // Set auxiliary variables - Thermostat[ctr_output].time_rampup_nextcycle = uptime + (uint32_t)Thermostat[ctr_output].time_rampup_cycle; + Thermostat[ctr_output].time_rampup_nextcycle = uptime + ((uint32_t)Thermostat[ctr_output].time_rampup_cycle * 60); Thermostat[ctr_output].temp_rampup_cycle = Thermostat[ctr_output].temp_measured; // Reset period counter Thermostat[ctr_output].counter_rampup_cycles = 1; @@ -785,7 +784,7 @@ void ThermostatWorkAutomaticRampUp(uint8_t ctr_output) // Increase the period counter Thermostat[ctr_output].counter_rampup_cycles++; // Set another period - Thermostat[ctr_output].time_rampup_nextcycle = uptime + (uint32_t)Thermostat[ctr_output].time_rampup_cycle; + Thermostat[ctr_output].time_rampup_nextcycle = uptime + ((uint32_t)Thermostat[ctr_output].time_rampup_cycle * 60); // Reset time_ctr_changepoint and temp_rampup_output_off Thermostat[ctr_output].time_ctr_changepoint = uptime + (60 * (uint32_t)Thermostat[ctr_output].time_rampup_max) - time_in_rampup; Thermostat[ctr_output].temp_rampup_output_off = Thermostat[ctr_output].temp_target_level_ctr; @@ -1256,7 +1255,7 @@ void CmndTempMeasuredGrdRead(void) else { value = Thermostat[ctr_output].temp_measured_gradient; } - ResponseCmndFloat((float)value / 10, 1); + ResponseCmndFloat(((float)value) / 1000, 1); } } @@ -1280,25 +1279,11 @@ void CmndTimeManualToAutoSet(void) uint8_t ctr_output = XdrvMailbox.index - 1; if (XdrvMailbox.data_len > 0) { uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat[ctr_output].time_manual_to_auto = (uint16_t)(value / 60); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_manual_to_auto = (uint16_t)value; } } - ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_manual_to_auto * 60)); - } -} - -void CmndTimeOnLimitSet(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { - uint8_t ctr_output = XdrvMailbox.index - 1; - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat[ctr_output].time_on_limit = (uint16_t)(value / 60); - } - } - ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_on_limit * 60)); + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_manual_to_auto)); } } @@ -1330,17 +1315,33 @@ void CmndTimeResetSet(void) } } +void CmndTimePiProportRead(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + ResponseCmndNumber((int)Thermostat[ctr_output].time_proportional_pi); + } +} + +void CmndTimePiIntegrRead(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { + uint8_t ctr_output = XdrvMailbox.index - 1; + ResponseCmndNumber((int)Thermostat[ctr_output].time_integral_pi); + } +} + void CmndTimePiCycleSet(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { uint8_t ctr_output = XdrvMailbox.index - 1; if (XdrvMailbox.data_len > 0) { uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat[ctr_output].time_pi_cycle = (uint16_t)(value / 60); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_pi_cycle = (uint16_t)value; } } - ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_pi_cycle * 60)); + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_pi_cycle)); } } @@ -1404,11 +1405,11 @@ void CmndTimeMaxActionSet(void) uint8_t ctr_output = XdrvMailbox.index - 1; if (XdrvMailbox.data_len > 0) { uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat[ctr_output].time_max_action = (uint16_t)(value / 60); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_max_action = (uint16_t)value; } } - ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_max_action * 60)); + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_max_action)); } } @@ -1418,11 +1419,11 @@ void CmndTimeMinActionSet(void) uint8_t ctr_output = XdrvMailbox.index - 1; if (XdrvMailbox.data_len > 0) { uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat[ctr_output].time_min_action = (uint16_t)(value / 60); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_min_action = (uint16_t)value; } } - ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_min_action * 60)); + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_min_action)); } } @@ -1432,11 +1433,11 @@ void CmndTimeSensLostSet(void) uint8_t ctr_output = XdrvMailbox.index - 1; if (XdrvMailbox.data_len > 0) { uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat[ctr_output].time_sens_lost = (uint16_t)(value / 60); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_sens_lost = (uint16_t)value; } } - ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_sens_lost * 60)); + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_sens_lost)); } } @@ -1446,11 +1447,11 @@ void CmndTimeMinTurnoffActionSet(void) uint8_t ctr_output = XdrvMailbox.index - 1; if (XdrvMailbox.data_len > 0) { uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat[ctr_output].time_min_turnoff_action = (uint16_t)(value / 60); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_min_turnoff_action = (uint16_t)value; } } - ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_min_turnoff_action * 60)); + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_min_turnoff_action)); } } @@ -1514,11 +1515,11 @@ void CmndTimeRampupMaxSet(void) uint8_t ctr_output = XdrvMailbox.index - 1; if (XdrvMailbox.data_len > 0) { uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat[ctr_output].time_rampup_max = (uint16_t)(value / 60); + if ((value >= 0) && (value <= 1440)) { + Thermostat[ctr_output].time_rampup_max = (uint16_t)value; } } - ResponseCmndNumber((int)(((uint32_t)Thermostat[ctr_output].time_rampup_max) * 60)); + ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_rampup_max)); } } @@ -1528,7 +1529,7 @@ void CmndTimeRampupCycleSet(void) uint8_t ctr_output = XdrvMailbox.index - 1; if (XdrvMailbox.data_len > 0) { uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 54000)) { + if ((value >= 0) && (value <= 1440)) { Thermostat[ctr_output].time_rampup_cycle = (uint16_t)value; } } @@ -1563,22 +1564,6 @@ void CmndTempRampupPiAccErrSet(void) } } -void CmndTimePiProportRead(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { - uint8_t ctr_output = XdrvMailbox.index - 1; - ResponseCmndNumber((int)Thermostat[ctr_output].time_proportional_pi); - } -} - -void CmndTimePiIntegrRead(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { - uint8_t ctr_output = XdrvMailbox.index - 1; - ResponseCmndNumber((int)Thermostat[ctr_output].time_integral_pi); - } -} - void CmndDiagnosticModeSet(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) { diff --git a/tasmota/xdrv_81_webcam.ino b/tasmota/xdrv_81_webcam.ino index 15c2dd4ba..95ec03cc4 100644 --- a/tasmota/xdrv_81_webcam.ino +++ b/tasmota/xdrv_81_webcam.ino @@ -354,8 +354,8 @@ uint32_t wc_get_jpeg(uint8_t **buff) { _jpg_buf_len = wc_fb->len; _jpg_buf = wc_fb->buf; } - esp_camera_fb_return(wc_fb); - *buff = _jpg_buf; + esp_camera_fb_return(wc_fb); // This frees the buffer + *buff = _jpg_buf; // Buffer has been freed so this will cause exceptions return _jpg_buf_len; } @@ -447,7 +447,7 @@ void HandleImage(void) { len = wc_get_jpeg(&buff); if (len) { client.write(buff,len); - free(buff); + free(buff); // Buffer has been freed already in wc_get_jpeg so this will cause exceptions } } else { bnum--; @@ -475,6 +475,50 @@ void handleMjpeg(void) { } } +void HandleImageTheo(void) { + if (!HttpCheckPriviledgedAccess(true)) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Capture image")); + + if (Settings.webcam_config.stream) { + if (!CamServer) { + WcStreamControl(); + } + } + + camera_fb_t *wc_fb; + wc_fb = esp_camera_fb_get(); // Acquire frame + if (!wc_fb) { + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Frame buffer could not be acquired")); + return; + } + + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + if (wc_fb->format != PIXFORMAT_JPEG) { + bool jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted) { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + } else { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + + if (_jpg_buf_len) { + Webserver->client().flush(); + WSHeaderSend(); + Webserver->sendHeader(F("Content-disposition"), F("inline; filename=cap.jpg")); + Webserver->send_P(200, "image/jpeg", (char *)_jpg_buf, _jpg_buf_len); + Webserver->client().stop(); + } + + esp_camera_fb_return(wc_fb); // Free frame buffer + + AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Image sent")); +} + #ifdef USE_FACE_DETECT static mtmn_config_t mtmn_config = {0}; @@ -812,6 +856,8 @@ void wc_loop(void) { void wc_pic_setup(void) { Webserver->on("/wc.jpg", HandleImage); Webserver->on("/wc.mjpeg", HandleImage); + + Webserver->on("/snapshot.jpg", HandleImageTheo); } /* diff --git a/tasmota/xdrv_99_debug.ino b/tasmota/xdrv_99_debug.ino index eeb4e7ca1..672ff17d3 100644 --- a/tasmota/xdrv_99_debug.ino +++ b/tasmota/xdrv_99_debug.ino @@ -59,13 +59,14 @@ #define D_CMND_I2CREAD "I2CRead" #define D_CMND_I2CSTRETCH "I2CStretch" #define D_CMND_I2CCLOCK "I2CClock" +#define D_CMND_SERBUFF "SerBufSize" const char kDebugCommands[] PROGMEM = "|" // No prefix D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" #ifdef USE_WEBSERVER D_CMND_CFGXOR "|" #endif - D_CMND_CPUCHECK "|" + D_CMND_CPUCHECK "|" D_CMND_SERBUFF "|" #ifdef DEBUG_THEO D_CMND_EXCEPTION "|" #endif @@ -80,7 +81,7 @@ void (* const DebugCommand[])(void) PROGMEM = { #ifdef USE_WEBSERVER &CmndCfgXor, #endif - &CmndCpuCheck, + &CmndCpuCheck, &CmndSerBufSize, #ifdef DEBUG_THEO &CmndException, #endif @@ -479,6 +480,18 @@ void CmndCpuCheck(void) ResponseCmndNumber(CPU_load_check); } +void CmndSerBufSize(void) +{ + if (XdrvMailbox.data_len > 0) { + Serial.setRxBufferSize(XdrvMailbox.payload); + } +#ifdef ESP8266 + ResponseCmndNumber(Serial.getRxBufferSize()); +#else + ResponseCmndDone(); +#endif +} + void CmndFreemem(void) { if (XdrvMailbox.data_len > 0) {