diff --git a/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.h index d45436b24..42c4c63bb 100644 --- a/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.h @@ -53,7 +53,7 @@ public: }; // this template method class is used to track the data being sent on the uart -// when using our own UART ISR +// when using our own UART ISR // used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes // class NeoEsp8266UartInterruptContext : NeoEsp8266UartContext @@ -77,12 +77,12 @@ public: private: volatile const uint8_t* _asyncBuff; volatile const uint8_t* _asyncBuffEnd; - volatile static NeoEsp8266UartInterruptContext* s_uartInteruptContext[2]; + volatile static NeoEsp8266UartInterruptContext* s_uartInteruptContext[2]; static void ICACHE_RAM_ATTR Isr(void* param); }; -// this template feature class is used a base for all others and contains +// this template feature class is used a base for all others and contains // common methods // class UartFeatureBase @@ -129,7 +129,7 @@ public: } }; -// this template method class is used a base for all others and contains +// this template method class is used a base for all others and contains // common properties and methods // // used by NeoEsp8266Uart and NeoEsp8266AsyncUart @@ -233,14 +233,14 @@ protected: } // detach context, which will disable intr, may disable ISR _context.Detach(T_UARTFEATURE::Index); - + free(_pixelsSending); } void ICACHE_RAM_ATTR InitializeUart(uint32_t uartBaud) { T_UARTFEATURE::Init(uartBaud); - + // attach the context, which will enable the ISR _context.Attach(T_UARTFEATURE::Index); } @@ -248,7 +248,7 @@ protected: void UpdateUart(bool maintainBufferConsistency) { // Instruct ESP8266 hardware uart to send the pixels asynchronously - _context.StartSending(T_UARTFEATURE::Index, + _context.StartSending(T_UARTFEATURE::Index, _pixels, _pixels + _sizePixels); @@ -264,7 +264,10 @@ protected: } // swap so the user can modify without affecting the async operation - std::swap(_pixelsSending, _pixels); +// std::swap(_pixelsSending, _pixels); + uint8_t *temp = _pixelsSending; + _pixelsSending = _pixels; + _pixels = temp; } private: @@ -383,7 +386,7 @@ private: }; }; -// uart 0 +// uart 0 typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart0Ws2812xMethod; typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart0Sk6812Method; typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart0Apa106Method; diff --git a/lib/SENDMAIL/sendemail.cpp b/lib/SENDMAIL/sendemail.cpp new file mode 100755 index 000000000..ab2c90ecd --- /dev/null +++ b/lib/SENDMAIL/sendemail.cpp @@ -0,0 +1,304 @@ +#include "sendemail.h" + +// enable serial debugging +//#define DEBUG_EMAIL_PORT Serial + +SendEmail::SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used) : + host(host), port(port), user(user), passwd(passwd), timeout(timeout), ssl(ssl), auth_used(auth_used), client(new WiFiClientSecure()) +{ + +} + + + +String SendEmail::readClient() +{ + String r = client->readStringUntil('\n'); + r.trim(); + while (client->available()) { + delay(0); + r += client->readString(); + } + return r; +} + +void SetSerialBaudrate(int baudrate); + +bool SendEmail::send(const String& from, const String& to, const String& subject, const String& msg) +{ + if (!host.length()) + { + return false; + } + client->setTimeout(timeout); + // smtp connect +#ifdef DEBUG_EMAIL_PORT + SetSerialBaudrate(115200); + DEBUG_EMAIL_PORT.print("Connecting: "); + DEBUG_EMAIL_PORT.print(host); + DEBUG_EMAIL_PORT.print(":"); + DEBUG_EMAIL_PORT.println(port); +#endif + +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) +#else + client->setInsecure(); + bool mfln = client->probeMaxFragmentLength(host.c_str(), port, 512); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.printf("MFLN supported: %s\n", mfln ? "yes" : "no"); +#endif + if (mfln) { + client->setBufferSizes(512, 512); + } +#endif + + + if (!client->connect(host.c_str(), port)) + { +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println("Connection failed"); +#endif + return false; + } + + String buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("220"))) + { + return false; + } + + + buffer = F("EHLO "); + buffer += client->localIP().toString(); + + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("250"))) + { + return false; + } + if (user.length()>0 && passwd.length()>0 ) + { + + //buffer = F("STARTTLS"); + //client->println(buffer); + +if (auth_used==1) { + // plain +#ifdef USE_PLAIN + buffer = F("AUTH PLAIN"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("334"))) + { + return false; + } + + char plainAuth[100]; + memset(plainAuth,sizeof(plainAuth),0); + plainAuth[0] = '\0'; + strcpy(&plainAuth[1], user.c_str()); + strcpy(&plainAuth[2+user.length()],passwd.c_str()); + const char* pA = (const char*)&plainAuth; + char buf[100]; + base64_encode(buf, pA, user.length()+passwd.length()+2); + client->println(buf); + +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buf); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("235"))) + { + return false; + } +#endif + +} else { + + buffer = F("AUTH LOGIN"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("334"))) + { + return false; + } + base64 b; + //buffer = user; + //buffer = b.encode(buffer); + buffer = b.encode(user); + + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + //DEBUG_EMAIL_PORT.println(user); + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("334"))) + { + return false; + } + //buffer = this->passwd; + //buffer = b.encode(buffer); + buffer = b.encode(passwd); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + //DEBUG_EMAIL_PORT.println(passwd); + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("235"))) + { + return false; + } +} + + } + + // smtp send mail + buffer = F("MAIL FROM:"); + buffer += from; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("250"))) + { + return false; + } + buffer = F("RCPT TO:"); + buffer += to; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("250"))) + { + return false; + } + + + buffer = F("DATA"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("354"))) + { + return false; + } + buffer = F("From: "); + buffer += from; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = F("To: "); + buffer += to; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = F("Subject: "); + buffer += subject; + buffer += F("\r\n"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = msg; + client->println(buffer); + client->println('.'); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = F("QUIT"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + return true; +} + +#ifdef USE_PLAIN +void SendEmail::a3_to_a4(unsigned char * a4, unsigned char * a3) { + a4[0] = (a3[0] & 0xfc) >> 2; + a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); + a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); + a4[3] = (a3[2] & 0x3f); +} + +int SendEmail::base64_encode(char *output, const char *input, int inputLen) { + const char* _b64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i = 0, j = 0; + int encLen = 0; + unsigned char a3[3]; + unsigned char a4[4]; + while(inputLen--) { + a3[i++] = *(input++); + if(i == 3) { + a3_to_a4(a4, a3); + for(i = 0; i < 4; i++) { + output[encLen++] = _b64_alphabet[a4[i]]; + } + i = 0; + } + } + if(i) { + for(j = i; j < 3; j++) { + a3[j] = '\0'; + } + a3_to_a4(a4, a3); + for(j = 0; j < i + 1; j++) { + output[encLen++] = _b64_alphabet[a4[j]]; + } + while((i++ < 3)) { + output[encLen++] = '='; + } + } + output[encLen] = '\0'; + return encLen; +} +#endif diff --git a/lib/SENDMAIL/sendemail.h b/lib/SENDMAIL/sendemail.h new file mode 100755 index 000000000..394612fc6 --- /dev/null +++ b/lib/SENDMAIL/sendemail.h @@ -0,0 +1,39 @@ +#ifndef __SENDEMAIL_H +#define __SENDEMAIL_H + +//#define DEBUG_EMAIL_PORT + +#include +#include +#include +#include + +class SendEmail +{ + private: + const String host; + const int port; + const String user; + const String passwd; + const int timeout; + const bool ssl; + const int auth_used; +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) + WiFiClient* client; +#else + // use bear ssl + // #include "WiFiClientSecureLightBearSSL.h" + // BearSSL::WiFiClientSecure_light *client; + BearSSL::WiFiClientSecure* client; + //WiFiClientSecure* client; +#endif + String readClient(); + void a3_to_a4(unsigned char * a4, unsigned char * a3); + int base64_encode(char *output, const char *input, int inputLen); + public: + SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used); + bool send(const String& from, const String& to, const String& subject, const String& msg); + ~SendEmail() {client->stop(); delete client;} +}; + +#endif diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 06bd9d956..aefb1b821 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,12 @@ /*********************************************************************************************\ + * 6.6.0.11 20190907 + * Change Settings crc calculation allowing short term backward compatibility + * + * 6.6.0.10 20190905 + * Redesign Tuya support by Shantur Rathore (#6353) + * Add command Reset 99 to reset bootcount to zero (#684, #6351) + * Change command Time 1/2/3 to select JSON time format ISO, ISO + Epoch or Epoch for legacy reason + * * 6.6.0.9 20190828 * Change theoretical baudrate range to 300..19660500 bps in 300 increments (#6294) * Add Full support of all protocols in IRremoteESP8266, to be used on dedicated-IR Tasmota version. Warning: +81k Flash when compiling with USE_IR_REMOTE_FULL @@ -6,6 +14,9 @@ * Add 'sonoff-ir' pre-packaged IR-dedicated firmware and 'sonoff-ircustom' to customize firmware with IR Full protocol support * Add Zigbee support phase 2 - cc2530 initialization and basic ZCL decoding * Add driver USE_SDM120_2 with Domoticz P1 Smart Meter functionality as future replacement for USE_SDM120 - Pls test and report + * Add command Power0 0/1/2/Off/On/Toggle to control all power outputs at once (#6340) + * Add time to more events (#6337) + * Add command Time 1/2/3 to select JSON time format ISO + Epoch, ISO or Epoch * * 6.6.0.8 20190827 * Add Tuya Energy monitoring by Shantur Rathore diff --git a/sonoff/i18n.h b/sonoff/i18n.h index 463ec13cf..35b51bd1f 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -96,6 +96,7 @@ #define D_JSON_LOW "Low" #define D_JSON_MAC "Mac" #define D_JSON_MASK "Mask" +#define D_JSON_MEMORY_ERROR "Memory error" #define D_JSON_MINIMAL "minimal" #define D_JSON_MODEL "Model" #define D_JSON_MQTT_COUNT "MqttCount" @@ -326,6 +327,7 @@ #define D_CMND_WEBCOLOR "WebColor" #define D_CMND_WEBSENSOR "WebSensor" #define D_CMND_EMULATION "Emulation" +#define D_CMND_SENDMAIL "Sendmail" // Commands xdrv_03_energy.ino #define D_CMND_POWERLOW "PowerLow" @@ -444,6 +446,10 @@ #define D_CMND_LATITUDE "Latitude" #define D_CMND_LONGITUDE "Longitude" +// Commands xdrv_16_tuyadimmer.ino + +#define D_CMND_TUYA_MCU "TuyaMCU" + // Commands xdrv_23_zigbee.ino #define D_CMND_ZIGBEEZNPSEND "ZigbeeZNPSend" #define D_JSON_ZIGBEEZNPRECEIVED "ZigbeeZNPReceived" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index 8bbdb8b35..d76460815 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -147,9 +147,9 @@ #define D_START "Start" #define D_STD_TIME "STD" #define D_STOP "Stop" -#define D_SUBNET_MASK "Maschera sottorete" +#define D_SUBNET_MASK "Maschera Subnet" #define D_SUBSCRIBE_TO "Sottoscrivi a" -#define D_UNSUBSCRIBE_FROM "Unsubscribe from" +#define D_UNSUBSCRIBE_FROM "Cancella da" #define D_SUCCESSFUL "Riuscito" #define D_SUNRISE "Alba" #define D_SUNSET "Tramonto" @@ -161,16 +161,16 @@ #define D_TRANSMIT "Trasmesso" #define D_TRUE "Vero" #define D_TVOC "TVOC" -#define D_UPGRADE "aggiornamento" +#define D_UPGRADE "Aggiornamento" #define D_UPLOAD "Invio" #define D_UPTIME "Uptime" #define D_USER "Utente" #define D_UTC_TIME "UTC" #define D_UV_INDEX "Indice UV" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" +#define D_UV_INDEX_1 "Basso" +#define D_UV_INDEX_2 "Medio" +#define D_UV_INDEX_3 "Alto" +#define D_UV_INDEX_4 "Pericolo" #define D_UV_INDEX_5 "BurnL1/2" #define D_UV_INDEX_6 "BurnL3" #define D_UV_INDEX_7 "OoR" @@ -186,7 +186,7 @@ #define D_WARNING_MINIMAL_VERSION "ATTENZIONE Questa versione non supporta il salvataggio delle impostazioni" #define D_LEVEL_10 "level 1-0" #define D_LEVEL_01 "level 0-1" -#define D_SERIAL_LOGGING_DISABLED "Log seriale disabilitato" +#define D_SERIAL_LOGGING_DISABLED "Log Seriale disabilitato" #define D_SYSLOG_LOGGING_REENABLED "Syslog ri-abilitato" #define D_SET_BAUDRATE_TO "Baudrate impostato a" @@ -198,11 +198,11 @@ #define D_OSWATCH "osWatch" #define D_BLOCKED_LOOP "Ciclo Bloccato" #define D_WPS_FAILED_WITH_STATUS "WPSconfig Fallito con stato" -#define D_ACTIVE_FOR_3_MINUTES "attivo per 3 minuti" +#define D_ACTIVE_FOR_3_MINUTES "Attivo per 3 minuti" #define D_FAILED_TO_START "partenza fallita" #define D_PATCH_ISSUE_2186 "Patch issue 2186" #define D_CONNECTING_TO_AP "Connessione ad AP" -#define D_IN_MODE "in modalità" +#define D_IN_MODE "In modalità" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Connessione fallita, indirizzo IP non ricevuto" #define D_CONNECT_FAILED_AP_NOT_REACHED "Connessione fallita, AP non raggiungibile" #define D_CONNECT_FAILED_WRONG_PASSWORD "Connessione fallita, password AP non corretta" @@ -259,8 +259,8 @@ #define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_ADC "ADC" #define D_GPIO "GPIO" -#define D_SERIAL_IN "Serial In" -#define D_SERIAL_OUT "Serial Out" +#define D_SERIAL_IN "Seriale In" +#define D_SERIAL_OUT "Seriale Out" #define D_WIFI_PARAMETERS "Parametri Wifi" #define D_SCAN_FOR_WIFI_NETWORKS "Scansione delle reti wifi" @@ -283,30 +283,30 @@ #define D_FULL_TOPIC "Full Topic" #define D_LOGGING_PARAMETERS "Parametri Logging" -#define D_SERIAL_LOG_LEVEL "Seriale livello di log" -#define D_WEB_LOG_LEVEL "Web livello di log" -#define D_SYS_LOG_LEVEL "Sys livello di log" +#define D_SERIAL_LOG_LEVEL "Livello di log Seriale" +#define D_WEB_LOG_LEVEL "livello di log Web" +#define D_SYS_LOG_LEVEL "livello di log Sys" #define D_MORE_DEBUG "Debug aggiuntivo" #define D_SYSLOG_HOST "Syslog host" #define D_SYSLOG_PORT "Syslog porta" #define D_TELEMETRY_PERIOD "Periodo Telemetria" #define D_OTHER_PARAMETERS "Altri parametri" -#define D_TEMPLATE "Template" -#define D_ACTIVATE "Activate" +#define D_TEMPLATE "Modello" +#define D_ACTIVATE "Attivare" #define D_WEB_ADMIN_PASSWORD "Password Amministratore Web" #define D_MQTT_ENABLE "Abilita MQTT" -#define D_FRIENDLY_NAME "Nome confidenziale" +#define D_FRIENDLY_NAME "Nome amichevole" #define D_BELKIN_WEMO "Belkin WeMo" #define D_HUE_BRIDGE "Hue Bridge" #define D_SINGLE_DEVICE "dispositivo singolo" #define D_MULTI_DEVICE "dispositivo multiplo" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" +#define D_CONFIGURE_TEMPLATE "Configurare Modello" +#define D_TEMPLATE_PARAMETERS "Parametri Modello" +#define D_TEMPLATE_NAME "Nome" +#define D_BASE_TYPE "Baseto nel" +#define D_TEMPLATE_FLAGS "Opzioni" #define D_SAVE_CONFIGURATION "Salva configurazione" #define D_CONFIGURATION_SAVED "Configurazione salvata" @@ -464,12 +464,12 @@ #define D_PARTICALS_BEYOND "Particelle" // xsns_32_mpu6050.ino -#define D_AX_AXIS "Accel. X-Axis" -#define D_AY_AXIS "Accel. Y-Axis" -#define D_AZ_AXIS "Accel. Z-Axis" -#define D_GX_AXIS "Gyro X-Axis" -#define D_GY_AXIS "Gyro Y-Axis" -#define D_GZ_AXIS "Gyro Z-Axis" +#define D_AX_AXIS "Accel. Asse-X" +#define D_AY_AXIS "Accel. Asse-Y" +#define D_AZ_AXIS "Accel. Asse-Z" +#define D_GX_AXIS "Gyro Asse-X" +#define D_GY_AXIS "Gyro Asse-Y" +#define D_GZ_AXIS "Gyro Asse-Z" // xsns_34_hx711.ino #define D_HX_CAL_REMOVE "Rimuovere peso" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index c62c9fe32..15ec61b9c 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -65,8 +65,8 @@ #define D_BY "por" // Write by me #define D_BYTES "Bytes" #define D_CELSIUS "Celsius" -#define D_CHANNEL "Channel" -#define D_CO2 "Dióxido de carbono" +#define D_CHANNEL "Canal" +#define D_CO2 "CO2" #define D_CODE "Código" // Button code #define D_COLDLIGHT "Luz fria" #define D_COMMAND "Comando" @@ -93,14 +93,14 @@ #define D_FALLBACK_TOPIC "Tópico para retornar" #define D_FALSE "Falso" #define D_FILE "Arquivo" -#define D_FLOW_RATE "Flow rate" +#define D_FLOW_RATE "Quociente de vazão" #define D_FREE_MEMORY "Memória livre" #define D_FREQUENCY "Frequência" #define D_GAS "Gás" #define D_GATEWAY "Gateway" #define D_GROUP "Grupo" -#define D_HOST "Anfitrião" -#define D_HOSTNAME "Nome do anfitrião" +#define D_HOST "Host" +#define D_HOSTNAME "Nome do Host" #define D_HUMIDITY "Umidade" #define D_ILLUMINANCE "Luminância" #define D_IMMEDIATE "Imediato" // Button immediate @@ -167,10 +167,10 @@ #define D_USER "Usuário" #define D_UTC_TIME "UTC" #define D_UV_INDEX "Índice UV" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" +#define D_UV_INDEX_1 "Baixo" +#define D_UV_INDEX_2 "Médio" +#define D_UV_INDEX_3 "Alto" +#define D_UV_INDEX_4 "Perigro" #define D_UV_INDEX_5 "BurnL1/2" #define D_UV_INDEX_6 "BurnL3" #define D_UV_INDEX_7 "OoR" @@ -178,7 +178,7 @@ #define D_UV_POWER "UV Power" #define D_VERSION "Versão" #define D_VOLTAGE "Voltagem" -#define D_WEIGHT "Weight" +#define D_WEIGHT "Peso" #define D_WARMLIGHT "Luz quente" #define D_WEB_SERVER "Servidor WEB" @@ -186,7 +186,7 @@ #define D_WARNING_MINIMAL_VERSION "AVISO: esta versão não supporta configurações persistentes" #define D_LEVEL_10 "nível 1-0" #define D_LEVEL_01 "nível 0-1" -#define D_SERIAL_LOGGING_DISABLED "Registro em série desabilitado" +#define D_SERIAL_LOGGING_DISABLED "Registro Serial desabilitado" #define D_SYSLOG_LOGGING_REENABLED "Registro do Syslog reativado" #define D_SET_BAUDRATE_TO "Ajuste da velocidade para" @@ -292,7 +292,7 @@ #define D_TELEMETRY_PERIOD "Período de telemetria" #define D_OTHER_PARAMETERS "Outros parâmetros" -#define D_TEMPLATE "Template" +#define D_TEMPLATE "Modelo" #define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Senha de WEB Admin" #define D_MQTT_ENABLE "MQTT habilitado" @@ -302,11 +302,11 @@ #define D_SINGLE_DEVICE "Dispositivo único" #define D_MULTI_DEVICE "Múltiplos dispositivos" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" +#define D_CONFIGURE_TEMPLATE "Configurar Modelo" +#define D_TEMPLATE_PARAMETERS "Parâmetros Modelo" +#define D_TEMPLATE_NAME "Nome" +#define D_BASE_TYPE "Baseado em" +#define D_TEMPLATE_FLAGS "Opções" #define D_SAVE_CONFIGURATION "Gravar configuração" #define D_CONFIGURATION_SAVED "Configuração gravada" @@ -347,10 +347,10 @@ #define D_UPLOAD_ERR_7 "Envio cancelado" #define D_UPLOAD_ERR_8 "Arquivo inválido" #define D_UPLOAD_ERR_9 "Arquivo muito grande" -#define D_UPLOAD_ERR_10 "Failed to init RF chip" -#define D_UPLOAD_ERR_11 "Failed to erase RF chip" -#define D_UPLOAD_ERR_12 "Failed to write to RF chip" -#define D_UPLOAD_ERR_13 "Failed to decode RF firmware" +#define D_UPLOAD_ERR_10 "Falha ao iniciar chip RF" +#define D_UPLOAD_ERR_11 "Falha ao apagar o chip RF" +#define D_UPLOAD_ERR_12 "Falha ao escrever o chip RF" +#define D_UPLOAD_ERR_13 "Falha ao decodificar o firmware de RF" #define D_UPLOAD_ERROR_CODE "Código de erro do envio" #define D_ENTER_COMMAND "Inserir comando" @@ -472,27 +472,27 @@ #define D_GZ_AXIS "Gyro Z-Axis" // xsns_34_hx711.ino -#define D_HX_CAL_REMOVE "Remove weigth" -#define D_HX_CAL_REFERENCE "Load reference weigth" -#define D_HX_CAL_DONE "Calibrated" -#define D_HX_CAL_FAIL "Calibration failed" -#define D_RESET_HX711 "Reset Scale" -#define D_CONFIGURE_HX711 "Configure Scale" -#define D_HX711_PARAMETERS "Scale parameters" -#define D_ITEM_WEIGHT "Item weight" -#define D_REFERENCE_WEIGHT "Reference weigth" -#define D_CALIBRATE "Calibrate" -#define D_CALIBRATION "Calibration" +#define D_HX_CAL_REMOVE "Remover peso" +#define D_HX_CAL_REFERENCE "Peso de referência de carga" +#define D_HX_CAL_DONE "Calibrado" +#define D_HX_CAL_FAIL "Falha na calibração" +#define D_RESET_HX711 "Redefinir escala" +#define D_CONFIGURE_HX711 "Configurar escala" +#define D_HX711_PARAMETERS "Parâmetros de escala" +#define D_ITEM_WEIGHT "Peso do Item" +#define D_REFERENCE_WEIGHT "Peso de referência" +#define D_CALIBRATE "Calibrar" +#define D_CALIBRATION "Calibração" //xsns_35_tx20.ino -#define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" -#define D_TX20_WIND_SPEED_MAX "Wind Speed Max" +#define D_TX20_WIND_DIRECTION "Direção do vento" +#define D_TX20_WIND_SPEED "Velocidade do vento" +#define D_TX20_WIND_SPEED_AVG "Velocidade média do vento" +#define D_TX20_WIND_SPEED_MAX "Velocidade do vento Máxima" #define D_TX20_NORTH "N" -#define D_TX20_EAST "E" +#define D_TX20_EAST "L" #define D_TX20_SOUTH "S" -#define D_TX20_WEST "W" +#define D_TX20_WEST "O" //xsns_43_hre.ino #define D_LOG_HRE "HRE: " @@ -657,37 +657,37 @@ #define D_LOG_WIFI "WIF: " // Wifi //SDM220 -#define D_PHASE_ANGLE "Phase Angle" -#define D_IMPORT_ACTIVE "Import Active" -#define D_EXPORT_ACTIVE "Export Active" -#define D_IMPORT_REACTIVE "Import Reactive" -#define D_EXPORT_REACTIVE "Export Reactive" -#define D_TOTAL_REACTIVE "Total Reactive" +#define D_PHASE_ANGLE "Ângulo de Fase" +#define D_IMPORT_ACTIVE "Importar Ativo" +#define D_EXPORT_ACTIVE "Exportar Ativo" +#define D_IMPORT_REACTIVE "Importar Reativo" +#define D_EXPORT_REACTIVE "Exportar Reativo" +#define D_TOTAL_REACTIVE "Reativo total" #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" //SOLAXX1 -#define D_PV1_VOLTAGE "PV1 Voltage" -#define D_PV1_CURRENT "PV1 Current" -#define D_PV1_POWER "PV1 Power" -#define D_PV2_VOLTAGE "PV2 Voltage" -#define D_PV2_CURRENT "PV2 Current" -#define D_PV2_POWER "PV2 Power" -#define D_SOLAR_POWER "Solar Power" -#define D_INVERTER_POWER "Inverter Power" +#define D_PV1_VOLTAGE "PV1 Voltagem" +#define D_PV1_CURRENT "PV1 Corrente" +#define D_PV1_POWER "PV1 Energia" +#define D_PV2_VOLTAGE "PV2 Voltagem" +#define D_PV2_CURRENT "PV2 Corrente" +#define D_PV2_POWER "PV2 Energia" +#define D_SOLAR_POWER "Energia Solar" +#define D_INVERTER_POWER "Potência do Inversor" #define D_STATUS "Status" -#define D_WAITING "Waiting" -#define D_CHECKING "Checking" -#define D_WORKING "Working" -#define D_FAILURE "Failure" -#define D_SOLAX_ERROR_0 "No Error Code" -#define D_SOLAX_ERROR_1 "Grid Lost Fault" -#define D_SOLAX_ERROR_2 "Grid Voltage Fault" -#define D_SOLAX_ERROR_3 "Grid Frequency Fault" -#define D_SOLAX_ERROR_4 "Pv Voltage Fault" -#define D_SOLAX_ERROR_5 "Isolation Fault" -#define D_SOLAX_ERROR_6 "Over Temperature Fault" -#define D_SOLAX_ERROR_7 "Fan Fault" -#define D_SOLAX_ERROR_8 "Other Device Fault" +#define D_WAITING "Esperando" +#define D_CHECKING "Verificando" +#define D_WORKING "Trabalhando" +#define D_FAILURE "Falha" +#define D_SOLAX_ERROR_0 "Nenhum código de erro" +#define D_SOLAX_ERROR_1 "Erro Grid Perdida" +#define D_SOLAX_ERROR_2 "Falha na Tensão da rede" +#define D_SOLAX_ERROR_3 "Falha na Frequência do Grid" +#define D_SOLAX_ERROR_4 "Pv Falha de Tensão" +#define D_SOLAX_ERROR_5 "Falha de Isolamento" +#define D_SOLAX_ERROR_6 "Falha de Temperatura excessiva" +#define D_SOLAX_ERROR_7 "Falha no Ventilador" +#define D_SOLAX_ERROR_8 "Outra falha do dispositivo" #endif // _LANGUAGE_PT_BR_H_ diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index f55fc774f..a18adc7b7 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -307,7 +307,7 @@ // -- Optional modules ---------------------------- #define USE_BUZZER // Add support for a buzzer (+0k6 code) #define USE_SONOFF_IFAN // Add support for Sonoff iFan02 and iFan03 (+2k code) -#define USE_TUYA_DIMMER // Add support for Tuya Serial Dimmer +#define USE_TUYA_MCU // Add support for Tuya Serial MCU #define TUYA_DIMMER_ID 0 // Default dimmer Id #define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) #define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) diff --git a/sonoff/settings.h b/sonoff/settings.h index 66dbe93d0..14b29cc50 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -78,7 +78,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t no_hold_retain : 1; // bit 12 (v6.4.1.19) - SetOption62 - Don't use retain flag on HOLD messages uint32_t no_power_feedback : 1; // bit 13 (v6.5.0.9) - SetOption63 - Don't scan relay power state at restart uint32_t use_underscore : 1; // bit 14 (v6.5.0.12) - SetOption64 - Enable "_" instead of "-" as sensor index separator - uint32_t tuya_disable_dimmer : 1; // bit 15 (v6.5.0.15) - SetOption65 - Enable or Disable Tuya Serial Dimmer control + uint32_t ex_tuya_disable_dimmer : 1; // bit 15 (v6.5.0.15) - SetOption65 - (Enable or Disable Tuya Serial Dimmer control) - free since 6.6.0.10 uint32_t tuya_dimmer_range_255 : 1; // bit 16 (v6.6.0.1) - SetOption66 - Enable or Disable Dimmer range 255 slider control uint32_t buzzer_enable : 1; // bit 17 (v6.6.0.1) - SetOption67 - Enable buzzer when available uint32_t pwm_multi_channels : 1; // bit 18 (v6.6.0.3) - SetOption68 - Enable multi-channels PWM instead of Color PWM @@ -105,8 +105,7 @@ typedef union { uint32_t spare01 : 1; uint32_t spare02 : 1; uint32_t spare03 : 1; - uint32_t spare04 : 1; - uint32_t spare05 : 1; + uint32_t time_format : 2; // (v6.6.0.9) - CMND_TIME uint32_t calc_resolution : 3; uint32_t weight_resolution : 2; uint32_t frequency_resolution : 2; @@ -186,6 +185,14 @@ typedef struct { uint32_t last_return_kWhtotal; } EnergyUsage; + +typedef struct { + uint8_t fnid = 0; + uint8_t dpid = 0; +} TuyaFnidDpidMap; + +const uint8_t MAX_TUYA_FUNCTIONS = 16; + /* struct SYSCFG { unsigned long cfg_holder; // 000 Pre v6 header @@ -364,6 +371,7 @@ struct SYSCFG { unsigned long energy_frequency_calibration; // 7C8 also used by HX711 to save last weight uint16_t web_refresh; // 7CC char mems[MAX_RULE_MEMS][10]; // 7CE +<<<<<<< HEAD char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b uint8_t data8[32]; // E00 uint16_t data16[16]; // E20 @@ -372,8 +380,15 @@ struct SYSCFG { uint8_t free_e20[432]; // E50 +======= + char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b + TuyaFnidDpidMap tuya_fnid_map[MAX_TUYA_FUNCTIONS]; // E00 32 bytes - // FFF last location + uint8_t free_e20[472]; // E20 +>>>>>>> upstream/development + + uint32_t cfg_timestamp; // FF8 + uint32_t cfg_crc32; // FFC } Settings; struct RTCRBT { diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 4bdae99ff..9534c0ab6 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -273,8 +273,8 @@ const uint32_t SETTINGS_LOCATION = SPIFFS_END; // No need for SPIFFS as it uses // Version 5.2 allow for more flash space const uint8_t CFG_ROTATES = 8; // Number of flash sectors used (handles uploads) -uint16_t settings_crc = 0; uint32_t settings_location = SETTINGS_LOCATION; +uint32_t settings_crc32 = 0; uint8_t *settings_buffer = nullptr; /********************************************************************************************/ @@ -318,19 +318,42 @@ bool SettingsBufferAlloc(void) return true; } -uint16_t GetSettingsCrc(void) +uint16_t GetCfgCrc16(uint8_t *bytes, uint32_t size) { uint16_t crc = 0; - uint8_t *bytes = (uint8_t*)&Settings; - // Fix miscalculation if previous Settings was 3584 and current Settings is 4096 as of 0x06060007 - uint32_t size = (Settings.version < 0x06060007) ? 3584 : sizeof(SYSCFG); for (uint32_t i = 0; i < size; i++) { if ((i < 14) || (i > 15)) { crc += bytes[i]*(i+1); } // Skip crc } return crc; } +uint16_t GetSettingsCrc(void) +{ + // Fix miscalculation if previous Settings was 3584 and current Settings is 4096 between 0x06060007 and 0x0606000A + uint32_t size = ((Settings.version < 0x06060007) || (Settings.version > 0x0606000A)) ? 3584 : sizeof(SYSCFG); + return GetCfgCrc16((uint8_t*)&Settings, size); +} + +uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size) +{ + // https://create.stephan-brumme.com/crc32/#bitwise + uint32_t crc = 0; + + while (size--) { + crc ^= *bytes++; + for (uint32_t j = 0; j < 8; j++) { + crc = (crc >> 1) ^ (-int(crc & 1) & 0xEDB88320); + } + } + return ~crc; +} + +uint32_t GetSettingsCrc32(void) +{ + return GetCfgCrc32((uint8_t*)&Settings, sizeof(SYSCFG) -4); // Skip crc32 +} + void SettingsSaveAll(void) { if (Settings.flag.save_state) { @@ -340,9 +363,6 @@ void SettingsSaveAll(void) } XsnsCall(FUNC_SAVE_BEFORE_RESTART); XdrvCall(FUNC_SAVE_BEFORE_RESTART); -#ifdef USE_EEPROM - EepromCommit(); -#endif SettingsSave(0); } @@ -366,7 +386,7 @@ void SettingsSave(uint8_t rotate) * stop_flash_rotate 1 = Allow only eeprom flash slot use (SetOption12 1) */ #ifndef FIRMWARE_MINIMAL - if ((GetSettingsCrc() != settings_crc) || rotate) { + if ((GetSettingsCrc32() != settings_crc32) || rotate) { if (1 == rotate) { // Use eeprom flash slot only and disable flash rotate from now on (upgrade) stop_flash_rotate = 1; } @@ -381,32 +401,19 @@ void SettingsSave(uint8_t rotate) settings_location = SETTINGS_LOCATION; } } - Settings.save_flag++; - Settings.cfg_size = sizeof(SYSCFG); - Settings.cfg_crc = GetSettingsCrc(); -#ifdef USE_EEPROM - if (SPIFFS_END == settings_location) { - uint8_t* flash_buffer; - flash_buffer = new uint8_t[SPI_FLASH_SEC_SIZE]; - if (eeprom_data && eeprom_size) { - size_t flash_offset = SPI_FLASH_SEC_SIZE - eeprom_size; - memcpy(flash_buffer + flash_offset, eeprom_data, eeprom_size); // Write dirty EEPROM data - } else { - ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)flash_buffer, SPI_FLASH_SEC_SIZE); // Read EEPROM area - } - memcpy(flash_buffer, &Settings, sizeof(Settings)); - ESP.flashEraseSector(settings_location); - ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)flash_buffer, SPI_FLASH_SEC_SIZE); - delete[] flash_buffer; + Settings.save_flag++; + if (UtcTime() > START_VALID_TIME) { + Settings.cfg_timestamp = UtcTime(); } else { - ESP.flashEraseSector(settings_location); - ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); + Settings.cfg_timestamp++; } -#else + Settings.cfg_size = sizeof(SYSCFG); + Settings.cfg_crc = GetSettingsCrc(); // Keep for backward compatibility in case of fall-back just after upgrade + Settings.cfg_crc32 = GetSettingsCrc32(); + ESP.flashEraseSector(settings_location); ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); -#endif // USE_EEPROM if (!stop_flash_rotate && rotate) { for (uint32_t i = 1; i < CFG_ROTATES; i++) { @@ -417,7 +424,7 @@ void SettingsSave(uint8_t rotate) AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_SAVED_TO_FLASH_AT " %X, " D_COUNT " %d, " D_BYTES " %d"), settings_location, Settings.save_flag, sizeof(SYSCFG)); - settings_crc = Settings.cfg_crc; + settings_crc32 = Settings.cfg_crc32; } #endif // FIRMWARE_MINIMAL RtcSettingsSave(); @@ -442,7 +449,10 @@ void SettingsLoad(void) bool valid = false; if (Settings.version > 0x06000000) { - bool almost_valid = (Settings.cfg_crc == GetSettingsCrc()); + bool almost_valid = (Settings.cfg_crc32 == GetSettingsCrc32()); + if (Settings.version < 0x0606000B) { + almost_valid = (Settings.cfg_crc == GetSettingsCrc()); + } // Sometimes CRC on pages below FB, overwritten by OTA, is fine but Settings are still invalid. So check cfg_holder too if (almost_valid && (0 == cfg_holder)) { cfg_holder = Settings.cfg_holder; } // At FB always active cfg_holder valid = (cfg_holder == Settings.cfg_holder); @@ -471,7 +481,7 @@ void SettingsLoad(void) if (!settings_location || (Settings.cfg_holder != (uint16_t)CFG_HOLDER)) { // Init defaults if cfg_holder differs from user settings in my_user_config.h SettingsDefault(); } - settings_crc = GetSettingsCrc(); + settings_crc32 = GetSettingsCrc32(); #endif // FIRMWARE_MINIMAL RtcSettingsLoad(); @@ -1096,6 +1106,41 @@ void SettingsDelta(void) Settings.sbaudrate = Settings.ex_sbaudrate * 4; } + if (Settings.version < 0x0606000A) { + uint8_t tuyaindex = 0; + if (Settings.param[P_ex_TUYA_DIMMER_ID] > 0) { // ex SetOption34 + Settings.tuya_fnid_map[tuyaindex].fnid = 21; // TUYA_MCU_FUNC_DIMMER - Move Tuya Dimmer Id to Map + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_DIMMER_ID]; + tuyaindex++; + } else if (Settings.flag3.ex_tuya_disable_dimmer == 1) { // ex SetOption65 + Settings.tuya_fnid_map[tuyaindex].fnid = 11; // TUYA_MCU_FUNC_REL1 - Create FnID for Switches + Settings.tuya_fnid_map[tuyaindex].dpid = 1; + tuyaindex++; + } + if (Settings.param[P_ex_TUYA_RELAYS] > 0) { + for (uint8_t i = 0 ; i < Settings.param[P_ex_TUYA_RELAYS]; i++) { // ex SetOption41 + Settings.tuya_fnid_map[tuyaindex].fnid = 12 + i; // TUYA_MCU_FUNC_REL2 - Create FnID for Switches + Settings.tuya_fnid_map[tuyaindex].dpid = i + 2; + tuyaindex++; + } + } + if (Settings.param[P_ex_TUYA_POWER_ID] > 0) { // ex SetOption46 + Settings.tuya_fnid_map[tuyaindex].fnid = 31; // TUYA_MCU_FUNC_POWER - Move Tuya Power Id to Map + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_POWER_ID]; + tuyaindex++; + } + if (Settings.param[P_ex_TUYA_VOLTAGE_ID] > 0) { // ex SetOption44 + Settings.tuya_fnid_map[tuyaindex].fnid = 33; // TUYA_MCU_FUNC_VOLTAGE - Move Tuya Voltage Id to Map + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_VOLTAGE_ID]; + tuyaindex++; + } + if (Settings.param[P_ex_TUYA_CURRENT_ID] > 0) { // ex SetOption45 + Settings.tuya_fnid_map[tuyaindex].fnid = 32; // TUYA_MCU_FUNC_CURRENT - Move Tuya Current Id to Map + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_CURRENT_ID]; + tuyaindex++; + } + + } Settings.version = VERSION; SettingsSave(1); } diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 6b0c52ce4..e773bf7a7 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -130,6 +130,8 @@ const uint32_t SERIAL_POLLING = 100; // Serial receive polling in ms const uint32_t ZIGBEE_POLLING = 100; // Serial receive polling in ms const uint8_t MAX_STATUS = 11; // Max number of status lines +const uint32_t START_VALID_TIME = 1451602800; // Time is synced and after 2016-01-01 + const uint32_t DRIVER_BOOT_DELAY = 1; // Number of milliseconds to retard driver cycles during boot-up time to reduce overall CPU load whilst Wifi is connecting const uint32_t LOOP_SLEEP_DELAY = 50; // Lowest number of milliseconds to go through the main loop using delay when needed @@ -231,7 +233,11 @@ enum EmulationOptions {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX}; enum TopicOptions { CMND, STAT, TELE, nu1, RESULT_OR_CMND, RESULT_OR_STAT, RESULT_OR_TELE }; -enum ExecuteCommandPowerOptions { POWER_OFF, POWER_ON, POWER_TOGGLE, POWER_BLINK, POWER_BLINK_STOP, power_nu1, POWER_OFF_NO_STATE, POWER_ON_NO_STATE, power_nu2, POWER_SHOW_STATE }; +enum ExecuteCommandPowerOptions { POWER_OFF, POWER_ON, POWER_TOGGLE, POWER_BLINK, POWER_BLINK_STOP, + POWER_OFF_NO_STATE = 8, POWER_ON_NO_STATE, POWER_TOGGLE_NO_STATE, + POWER_SHOW_STATE = 16 }; +enum SendKeyPowerOptions { POWER_HOLD = 3, CLEAR_RETAIN = 9 }; +enum SendKeyOptions { KEY_BUTTON, KEY_SWITCH }; enum PowerOnStateOptions { POWER_ALL_OFF, POWER_ALL_ON, POWER_ALL_SAVED_TOGGLE, POWER_ALL_SAVED, POWER_ALL_ALWAYS_ON, POWER_ALL_OFF_PULSETIME_ON }; @@ -239,9 +245,9 @@ enum ButtonStates { PRESSED, NOT_PRESSED }; enum Shortcuts { SC_CLEAR, SC_DEFAULT, SC_USER }; -enum SettingsParamIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_IR_UNKNOW_THRESHOLD, // SetOption32 .. SetOption38 - P_CSE7766_INVALID_POWER, P_HOLD_IGNORE, P_TUYA_RELAYS, P_OVER_TEMP, // SetOption39 .. SetOption42 - P_TUYA_DIMMER_MAX, P_TUYA_VOLTAGE_ID, P_TUYA_CURRENT_ID, P_TUYA_POWER_ID, // SetOption43 .. SetOption46 +enum SettingsParamIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_ex_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_IR_UNKNOW_THRESHOLD, // SetOption32 .. SetOption38 + P_CSE7766_INVALID_POWER, P_HOLD_IGNORE, P_ex_TUYA_RELAYS, P_OVER_TEMP, // SetOption39 .. SetOption42 + P_TUYA_DIMMER_MAX, P_ex_TUYA_VOLTAGE_ID, P_ex_TUYA_CURRENT_ID, P_ex_TUYA_POWER_ID, // SetOption43 .. SetOption46 P_ENERGY_TARIFF1, P_ENERGY_TARIFF2, // SetOption47 .. SetOption48 P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 @@ -257,7 +263,7 @@ enum LightTypes { LT_BASIC, LT_PWM1, LT_PWM2, LT_PWM3, LT_PWM4, LT enum LightSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX}; enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_PIN_STATE, FUNC_MODULE_INIT, FUNC_PRE_INIT, FUNC_INIT, - FUNC_LOOP, FUNC_EVERY_50_MSECOND, FUNC_EVERY_100_MSECOND, FUNC_EVERY_200_MSECOND, FUNC_EVERY_250_MSECOND, FUNC_EVERY_SECOND, + FUNC_LOOP, FUNC_EVERY_50_MSECOND, FUNC_EVERY_100_MSECOND, FUNC_EVERY_200_MSECOND, FUNC_EVERY_250_MSECOND, FUNC_EVERY_300_MSECOND, FUNC_EVERY_SECOND, FUNC_SAVE_AT_MIDNIGHT, FUNC_SAVE_BEFORE_RESTART, FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_SENSOR, FUNC_COMMAND, FUNC_COMMAND_SENSOR, FUNC_COMMAND_DRIVER, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index f45411b37..f6641e5ce 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -90,6 +90,7 @@ unsigned long pulse_timer[MAX_PULSETIMERS] = { 0 }; // Power off timer unsigned long blink_timer = 0; // Power cycle timer unsigned long backlog_delay = 0; // Command backlog delay power_t power = 0; // Current copy of Settings.power +power_t last_power = 0; // Last power set state power_t blink_power; // Blink power state power_t blink_mask = 0; // Blink relay active mask power_t blink_powersave; // Blink start power save state @@ -172,7 +173,7 @@ String backlog[MAX_BACKLOG]; // Command backlog char* Format(char* output, const char* input, int size) { char *token; - uint8_t digits = 0; + uint32_t digits = 0; if (strstr(input, "%") != nullptr) { strlcpy(output, input, size); @@ -201,7 +202,9 @@ char* Format(char* output, const char* input, int size) } } } - if (!digits) { strlcpy(output, input, size); } + if (!digits) { + strlcpy(output, input, size); + } return output; } @@ -219,7 +222,7 @@ char* GetOtaUrl(char *otaurl, size_t otaurl_size) return otaurl; } -char* GetTopic_P(char *stopic, uint8_t prefix, char *topic, const char* subtopic) +char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic) { /* prefix 0 = Cmnd prefix 1 = Stat @@ -258,25 +261,29 @@ char* GetTopic_P(char *stopic, uint8_t prefix, char *topic, const char* subtopic } fulltopic.replace(F("#"), ""); fulltopic.replace(F("//"), "/"); - if (!fulltopic.endsWith("/")) fulltopic += "/"; + if (!fulltopic.endsWith("/")) { + fulltopic += "/"; + } snprintf_P(stopic, TOPSZ, PSTR("%s%s"), fulltopic.c_str(), romram); return stopic; } -char* GetFallbackTopic_P(char *stopic, uint8_t prefix, const char* subtopic) +char* GetFallbackTopic_P(char *stopic, uint32_t prefix, const char* subtopic) { return GetTopic_P(stopic, prefix +4, nullptr, subtopic); } -char* GetStateText(uint8_t state) +char* GetStateText(uint32_t state) { - if (state > 3) { state = 1; } + if (state > 3) { + state = 1; + } return Settings.state_text[state]; } /********************************************************************************************/ -void SetLatchingRelay(power_t lpower, uint8_t state) +void SetLatchingRelay(power_t lpower, uint32_t state) { // power xx00 - toggle REL1 (Off) and REL3 (Off) - device 1 Off, device 2 Off // power xx01 - toggle REL2 (On) and REL3 (Off) - device 1 On, device 2 Off @@ -289,17 +296,15 @@ void SetLatchingRelay(power_t lpower, uint8_t state) } for (uint32_t i = 0; i < devices_present; i++) { - uint8_t port = (i << 1) + ((latching_power >> i) &1); + uint32_t port = (i << 1) + ((latching_power >> i) &1); if (pin[GPIO_REL1 +port] < 99) { digitalWrite(pin[GPIO_REL1 +port], bitRead(rel_inverted, port) ? !state : state); } } } -void SetDevicePower(power_t rpower, int source) +void SetDevicePower(power_t rpower, uint32_t source) { - uint8_t state; - ShowSource(source); if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { // All on and stay on @@ -310,9 +315,11 @@ void SetDevicePower(power_t rpower, int source) if (Settings.flag.interlock) { // Allow only one or no relay set for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { power_t mask = 1; - uint8_t count = 0; + uint32_t count = 0; for (uint32_t j = 0; j < devices_present; j++) { - if ((Settings.interlock[i] & mask) && (rpower & mask)) { count++; } + if ((Settings.interlock[i] & mask) && (rpower & mask)) { + count++; + } mask <<= 1; } if (count > 1) { @@ -323,6 +330,10 @@ void SetDevicePower(power_t rpower, int source) } } + if (rpower) { // Any power set + last_power = rpower; + } + XdrvMailbox.index = rpower; XdrvCall(FUNC_SET_POWER); // Signal power state @@ -344,7 +355,7 @@ void SetDevicePower(power_t rpower, int source) } else { for (uint32_t i = 0; i < devices_present; i++) { - state = rpower &1; + power_t state = rpower &1; if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? !state : state); } @@ -353,13 +364,59 @@ void SetDevicePower(power_t rpower, int source) } } -void SetLedPowerIdx(uint8_t led, uint8_t state) +void RestorePower(bool publish_power, uint32_t source) { - if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present - if (pin[GPIO_LED2] < 99) { led = 1; } + if (power != last_power) { + SetDevicePower(last_power, source); + if (publish_power) { + MqttPublishAllPowerState(); + } + } +} + +void SetAllPower(uint32_t state, uint32_t source) +{ +// state 0 = POWER_OFF = Relay Off +// state 1 = POWER_ON = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled) +// state 2 = POWER_TOGGLE = Toggle relay +// state 8 = POWER_OFF_NO_STATE = Relay Off and no publishPowerState +// state 9 = POWER_ON_NO_STATE = Relay On and no publishPowerState +// state 10 = POWER_TOGGLE_NO_STATE = Toggle relay and no publishPowerState +// state 16 = POWER_SHOW_STATE = Show power state + + bool publish_power = true; + if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { + state &= 3; // POWER_OFF, POWER_ON or POWER_TOGGLE + publish_power = false; + } + if ((state >= POWER_OFF) && (state <= POWER_TOGGLE)) { + power_t all_on = (1 << devices_present) -1; + switch (state) { + case POWER_OFF: + power = 0; + break; + case POWER_ON: + power = all_on; + break; + case POWER_TOGGLE: + power ^= all_on; // Complement current state + } + SetDevicePower(power, source); + } + if (publish_power) { + MqttPublishAllPowerState(); + } +} + +void SetLedPowerIdx(uint32_t led, uint32_t state) +{ + if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present + if (pin[GPIO_LED2] < 99) { + led = 1; + } } if (pin[GPIO_LED1 + led] < 99) { - uint8_t mask = 1 << led; + uint32_t mask = 1 << led; if (state) { state = 1; led_power |= mask; @@ -370,13 +427,13 @@ void SetLedPowerIdx(uint8_t led, uint8_t state) } } -void SetLedPower(uint8_t state) +void SetLedPower(uint32_t state) { - if (99 == pin[GPIO_LEDLNK]) { // Legacy - Only use LED1 and/or LED2 + if (99 == pin[GPIO_LEDLNK]) { // Legacy - Only use LED1 and/or LED2 SetLedPowerIdx(0, state); } else { power_t mask = 1; - for (uint32_t i = 0; i < leds_present; i++) { // Map leds to power + for (uint32_t i = 0; i < leds_present; i++) { // Map leds to power bool tstate = (power & mask); SetLedPowerIdx(i, tstate); mask <<= 1; @@ -384,18 +441,18 @@ void SetLedPower(uint8_t state) } } -void SetLedPowerAll(uint8_t state) +void SetLedPowerAll(uint32_t state) { for (uint32_t i = 0; i < leds_present; i++) { SetLedPowerIdx(i, state); } } -void SetLedLink(uint8_t state) +void SetLedLink(uint32_t state) { - uint8_t led_pin = pin[GPIO_LEDLNK]; - uint8_t led_inv = ledlnk_inverted; - if (99 == led_pin) { // Legacy - LED1 is status + uint32_t led_pin = pin[GPIO_LEDLNK]; + uint32_t led_inv = ledlnk_inverted; + if (99 == led_pin) { // Legacy - LED1 is status led_pin = pin[GPIO_LED1]; led_inv = bitRead(led_inverted, 0); } @@ -405,34 +462,32 @@ void SetLedLink(uint8_t state) } } -void SetPulseTimer(uint8_t index, uint16_t time) +void SetPulseTimer(uint32_t index, uint32_t time) { pulse_timer[index] = (time > 111) ? millis() + (1000 * (time - 100)) : (time > 0) ? millis() + (100 * time) : 0L; } -uint16_t GetPulseTimer(uint8_t index) +uint32_t GetPulseTimer(uint32_t index) { - uint16_t result = 0; - long time = TimePassedSince(pulse_timer[index]); if (time < 0) { time *= -1; - result = (time > 11100) ? (time / 1000) + 100 : (time > 0) ? time / 100 : 0; + return (time > 11100) ? (time / 1000) + 100 : (time > 0) ? time / 100 : 0; } - return result; + return 0; } /********************************************************************************************/ -bool SendKey(uint8_t key, uint8_t device, uint8_t state) +bool SendKey(uint32_t key, uint32_t device, uint32_t state) { -// key 0 = button_topic -// key 1 = switch_topic -// state 0 = off -// state 1 = on -// state 2 = toggle -// state 3 = hold -// state 9 = clear retain flag +// key 0 = KEY_BUTTON = button_topic +// key 1 = KEY_SWITCH = switch_topic +// state 0 = POWER_OFF = off +// state 1 = POWER_ON = on +// state 2 = POWER_TOGGLE = toggle +// state 3 = POWER_HOLD = hold +// state 9 = CLEAR_RETAIN = clear retain flag char stopic[TOPSZ]; char scommand[CMDSZ]; @@ -442,23 +497,25 @@ bool SendKey(uint8_t key, uint8_t device, uint8_t state) char *tmp = (key) ? Settings.switch_topic : Settings.button_topic; Format(key_topic, tmp, sizeof(key_topic)); if (Settings.flag.mqtt_enabled && MqttIsConnected() && (strlen(key_topic) != 0) && strcmp(key_topic, "0")) { - if (!key && (device > devices_present)) { device = 1; } // Only allow number of buttons up to number of devices + if (!key && (device > devices_present)) { + device = 1; // Only allow number of buttons up to number of devices + } GetTopic_P(stopic, CMND, key_topic, GetPowerDevice(scommand, device, sizeof(scommand), (key + Settings.flag.device_index_enable))); // cmnd/switchtopic/POWERx - if (9 == state) { + if (CLEAR_RETAIN == state) { mqtt_data[0] = '\0'; } else { - if ((Settings.flag3.button_switch_force_local || !strcmp(mqtt_topic, key_topic) || !strcmp(Settings.mqtt_grptopic, key_topic)) && (2 == state)) { - state = ~(power >> (device -1)) &1; + if ((Settings.flag3.button_switch_force_local || !strcmp(mqtt_topic, key_topic) || !strcmp(Settings.mqtt_grptopic, key_topic)) && (POWER_TOGGLE == state)) { + state = ~(power >> (device -1)) &1; // POWER_OFF or POWER_ON } snprintf_P(mqtt_data, sizeof(mqtt_data), GetStateText(state)); } #ifdef USE_DOMOTICZ if (!(DomoticzSendKey(key, device, state, strlen(mqtt_data)))) { - MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain) && (state != 3 || !Settings.flag3.no_hold_retain)); + MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain) && (state != POWER_HOLD || !Settings.flag3.no_hold_retain)); } #else - MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain) && (state != 3 || !Settings.flag3.no_hold_retain)); + MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain) && (state != POWER_HOLD || !Settings.flag3.no_hold_retain)); #endif // USE_DOMOTICZ result = !Settings.flag3.button_switch_force_local; } else { @@ -471,17 +528,18 @@ bool SendKey(uint8_t key, uint8_t device, uint8_t state) return result; } -void ExecuteCommandPower(uint8_t device, uint8_t state, int source) +void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) { // device = Relay number 1 and up -// state 0 = Relay Off -// state 1 = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled) -// state 2 = Toggle relay -// state 3 = Blink relay -// state 4 = Stop blinking relay -// state 6 = Relay Off and no publishPowerState -// state 7 = Relay On and no publishPowerState -// state 9 = Show power state +// state 0 = POWER_OFF = Relay Off +// state 1 = POWER_ON = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled) +// state 2 = POWER_TOGGLE = Toggle relay +// state 3 = POWER_BLINK = Blink relay +// state 4 = POWER_BLINK_STOP = Stop blinking relay +// state 8 = POWER_OFF_NO_STATE = Relay Off and no publishPowerState +// state 9 = POWER_ON_NO_STATE = Relay On and no publishPowerState +// state 10 = POWER_TOGGLE_NO_STATE = Toggle relay and no publishPowerState +// state 16 = POWER_SHOW_STATE = Show power state // ShowSource(source); @@ -495,16 +553,20 @@ void ExecuteCommandPower(uint8_t device, uint8_t state, int source) } #endif // USE_SONOFF_IFAN - uint8_t publish_power = 1; - if ((POWER_OFF_NO_STATE == state) || (POWER_ON_NO_STATE == state)) { - state &= 1; - publish_power = 0; + bool publish_power = true; + if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { + state &= 3; // POWER_OFF, POWER_ON or POWER_TOGGLE + publish_power = false; } - if ((device < 1) || (device > devices_present)) device = 1; + if ((device < 1) || (device > devices_present)) { + device = 1; + } active_device = device; - if (device <= MAX_PULSETIMERS) { SetPulseTimer(device -1, 0); } + if (device <= MAX_PULSETIMERS) { + SetPulseTimer(device -1, 0); + } power_t mask = 1 << (device -1); // Device to control if (state <= POWER_TOGGLE) { if ((blink_mask & mask)) { @@ -546,7 +608,9 @@ void ExecuteCommandPower(uint8_t device, uint8_t state, int source) #ifdef USE_KNX KnxUpdatePowerState(device, power); #endif // USE_KNX - if (publish_power && Settings.flag3.hass_tele_on_power) { MqttPublishTeleState(); } + if (publish_power && Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } if (device <= MAX_PULSETIMERS) { // Restart PulseTime if powered On SetPulseTimer(device -1, (((POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? ~power : power) & mask) ? Settings.pulse_timer[device -1] : 0); } @@ -563,13 +627,17 @@ void ExecuteCommandPower(uint8_t device, uint8_t state, int source) return; } else if (POWER_BLINK_STOP == state) { - uint8_t flag = (blink_mask & mask); + bool flag = (blink_mask & mask); blink_mask &= (POWER_MASK ^ mask); // Clear device mask MqttPublishPowerBlinkState(device); - if (flag) ExecuteCommandPower(device, (blink_powersave >> (device -1))&1, SRC_IGNORE); // Restore state + if (flag) { + ExecuteCommandPower(device, (blink_powersave >> (device -1))&1, SRC_IGNORE); // Restore state + } return; } - if (publish_power) MqttPublishPowerState(device); + if (publish_power) { + MqttPublishPowerState(device); + } } void StopAllPowerBlink(void) @@ -586,18 +654,6 @@ void StopAllPowerBlink(void) } } -void SetAllPower(uint8_t state, int source) -{ - if ((POWER_ALL_OFF == state) || (POWER_ALL_ON == state)) { - power = 0; - if (POWER_ALL_ON == state) { - power = (1 << devices_present) -1; - } - SetDevicePower(power, source); - MqttPublishAllPowerState(); - } -} - void MqttShowPWMState(void) { ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{")); @@ -824,7 +880,7 @@ void Every250mSeconds(void) { // As the max amount of sleep = 250 mSec this loop should always be taken... - uint8_t blinkinterval = 1; + uint32_t blinkinterval = 1; state_250mS++; state_250mS &= 0x3; @@ -1178,7 +1234,7 @@ void SerialInput(void) if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed char hex_char[(serial_in_byte_counter * 2) + 2]; - Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), + ResponseTime_P(PSTR(",\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), (Settings.flag.mqtt_serial_raw) ? ToHex_P((unsigned char*)serial_in_buffer, serial_in_byte_counter, hex_char, sizeof(hex_char)) : serial_in_buffer); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); XdrvRulesProcess(); @@ -1190,10 +1246,10 @@ void SerialInput(void) void GpioInit(void) { - uint8_t mpin; + uint32_t mpin; if (!ValidModule(Settings.module)) { - uint8_t module = MODULE; + uint32_t module = MODULE; if (!ValidModule(MODULE)) { module = SONOFF_BASIC; } Settings.module = module; Settings.last_module = module; @@ -1230,7 +1286,7 @@ void GpioInit(void) my_adc0 = Settings.my_adc0; // Set User selected Module sensors } my_module_flag = ModuleFlag(); - uint8_t template_adc0 = my_module_flag.data &15; + uint32_t template_adc0 = my_module_flag.data &15; if ((template_adc0 > ADC0_NONE) && (template_adc0 < ADC0_USER)) { my_adc0 = template_adc0; // Force Template override } @@ -1604,8 +1660,6 @@ void setup(void) XsnsCall(FUNC_INIT); } -uint32_t _counter = 0; - void loop(void) { uint32_t my_sleep = millis(); diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index cc2b8b237..271325da1 100644 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -85,7 +85,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c // -- Optional modules ------------------------- #define USE_SONOFF_IFAN // Add support for Sonoff iFan02 and iFan03 (+2k code) -#define USE_TUYA_DIMMER // Add support for Tuya Serial Dimmer +#define USE_TUYA_MCU // Add support for Tuya Serial MCU #ifndef TUYA_DIMMER_ID #define TUYA_DIMMER_ID 0 // Default dimmer Id #endif @@ -217,7 +217,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c //#ifndef USE_SONOFF_IFAN #define USE_SONOFF_IFAN // Add support for Sonoff iFan02 and iFan03 (+2k code) //#endif -#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer +#undef USE_TUYA_MCU // Disable support for Tuya Serial MCU #undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) #undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) #undef ROTARY_V1 // Disable support for MI Desk Lamp @@ -345,7 +345,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c // -- Optional modules ------------------------- #undef USE_BUZZER // Disable support for a buzzer (+0k6 code) #undef USE_SONOFF_IFAN // Disable support for Sonoff iFan02 and iFan03 (+2k code) -#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer +#undef USE_TUYA_MCU // Disable support for Tuya Serial MCU #undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) #undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) #undef USE_DS18x20 // Disable Optional for more than one DS18x20 sensors with id sort, single scan and read retry (+1k3 code) @@ -436,7 +436,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c // -- Optional modules ------------------------- #define USE_SONOFF_IFAN // Add support for Sonoff iFan02 and iFan03 (+2k code) -//#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer +//#undef USE_TUYA_MCU // Disable support for Tuya Serial MCU #undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) #undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) #undef ROTARY_V1 // Disable support for MI Desk Lamp @@ -517,7 +517,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c // -- Optional modules ------------------------- #undef USE_SONOFF_IFAN // Disable support for Sonoff iFan02 and iFan03 (+2k code) -#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer +#undef USE_TUYA_MCU // Disable support for Tuya Serial MCU #undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) #undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) #undef ROTARY_V1 // Disable support for MI Desk Lamp diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 85f1a8c8d..cdfe75e23 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -541,7 +541,7 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_SM16716_DAT, // SM16716 DATA GPIO_SM16716_SEL, // SM16716 SELECT #endif // USE_SM16716 -#ifdef USE_TUYA_DIMMER +#ifdef USE_TUYA_MCU GPIO_TUYA_TX, // Tuya Serial interface GPIO_TUYA_RX, // Tuya Serial interface #endif @@ -749,7 +749,7 @@ const uint8_t kModuleNiceList[] PROGMEM = { OBI2, MANZOKU_EU_4, ESP_SWITCH, // Switch Devices -#ifdef USE_TUYA_DIMMER +#ifdef USE_TUYA_MCU TUYA_DIMMER, // Dimmer Devices #endif #ifdef USE_ARMTRONIX_DIMMERS @@ -1728,7 +1728,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On ) 0, 0, 0 }, - { "Tuya Dimmer", // Tuya Dimmer (ESP8266 w/ separate MCU dimmer) + { "Tuya MCU", // Tuya MCU device (ESP8266 w/ separate MCU) // https://www.amazon.com/gp/product/B07CTNSZZ8/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1 GPIO_USER, // Virtual Button (controlled by MCU) GPIO_USER, // GPIO01 MCU serial control diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index e614fbf36..b7eae8a8f 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,6 +20,6 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -const uint32_t VERSION = 0x06060009; +const uint32_t VERSION = 0x0606000B; #endif // _SONOFF_VERSION_H_ diff --git a/sonoff/support.ino b/sonoff/support.ino index 4ada71ad2..035a1d31d 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -870,7 +870,24 @@ uint32_t WebColor(uint32_t i) * Response data handling \*********************************************************************************************/ -int Response_P(const char* format, ...) // Content send snprintf_P char data +const uint16_t TIMESZ = 100; // Max number of characters in time string + +char* ResponseGetTime(uint32_t format, char* time_str) +{ + switch (format) { + case 1: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%u"), GetDateAndTime(DT_LOCAL).c_str(), UtcTime()); + break; + case 2: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":%u"), UtcTime()); + break; + default: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); + } + return time_str; +} + +int Response_P(const char* format, ...) // Content send snprintf_P char data { // This uses char strings. Be aware of sending %% if % is needed va_list args; @@ -880,6 +897,20 @@ int Response_P(const char* format, ...) // Content send snprintf_P char data return len; } +int ResponseTime_P(const char* format, ...) // Content send snprintf_P char data +{ + // This uses char strings. Be aware of sending %% if % is needed + va_list args; + va_start(args, format); + + ResponseGetTime(Settings.flag2.time_format, mqtt_data); + + int mlen = strlen(mqtt_data); + int len = vsnprintf_P(mqtt_data + mlen, sizeof(mqtt_data) - mlen, format, args); + va_end(args); + return len + mlen; +} + int ResponseAppend_P(const char* format, ...) // Content send snprintf_P char data { // This uses char strings. Be aware of sending %% if % is needed @@ -891,15 +922,15 @@ int ResponseAppend_P(const char* format, ...) // Content send snprintf_P char d return len + mlen; } -int ResponseAppendTime(void) +int ResponseAppendTimeFormat(uint32_t format) { - return ResponseAppend_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%u"), GetDateAndTime(DT_LOCAL).c_str(), UtcTime()); + char time_str[TIMESZ]; + return ResponseAppend_P(ResponseGetTime(format, time_str)); } -int ResponseBeginTime(void) +int ResponseAppendTime(void) { - mqtt_data[0] = '\0'; - return ResponseAppendTime(); + return ResponseAppendTimeFormat(Settings.flag2.time_format); } int ResponseJsonEnd(void) diff --git a/sonoff/support_button.ino b/sonoff/support_button.ino index c32011b42..5c8db5d09 100644 --- a/sonoff/support_button.ino +++ b/sonoff/support_button.ino @@ -165,7 +165,7 @@ void ButtonHandler(void) if (!Button.hold_timer[button_index]) { button_pressed = true; } // Do not allow within 1 second } if (button_pressed) { - if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally } } @@ -174,7 +174,7 @@ void ButtonHandler(void) if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); - if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally } } else { @@ -199,14 +199,14 @@ void ButtonHandler(void) if (Settings.flag.button_restrict) { // SetOption1 (0) - Button restriction if (Settings.param[P_HOLD_IGNORE] > 0) { // SetOption40 (0) - Do not ignore button hold if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { - Button.hold_timer[button_index] = 0; // Reset button hold counter to stay below hold trigger - Button.press_counter[button_index] = 0; // Discard button press to disable functionality + Button.hold_timer[button_index] = 0; // Reset button hold counter to stay below hold trigger + Button.press_counter[button_index] = 0; // Discard button press to disable functionality DEBUG_CORE_LOG(PSTR("BTN: " D_BUTTON "%d cancel by " D_CMND_SETOPTION "40 %d"), button_index +1, Settings.param[P_HOLD_IGNORE]); } } if (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button hold Button.press_counter[button_index] = 0; - SendKey(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set + SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); // Execute Hold command via MQTT if ButtonTopic is set } } else { if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button held for factor times longer @@ -241,7 +241,7 @@ void ButtonHandler(void) #if defined(USE_LIGHT) && defined(ROTARY_V1) if (!((0 == button_index) && RotaryButtonPressed())) { #endif - if (single_press && SendKey(0, button_index + Button.press_counter[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + if (single_press && SendKey(KEY_BUTTON, button_index + Button.press_counter[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set // Success } else { if (Button.press_counter[button_index] < 3) { // Single or Double press diff --git a/sonoff/support_command.ino b/sonoff/support_command.ino index 6c2dc78a8..8497497ab 100644 --- a/sonoff/support_command.ino +++ b/sonoff/support_command.ino @@ -257,11 +257,20 @@ void CmndDelay(void) void CmndPower(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= devices_present)) { - if ((XdrvMailbox.payload < 0) || (XdrvMailbox.payload > 4)) { XdrvMailbox.payload = 9; } -// Settings.flag.device_index_enable = user_index; + if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_BLINK_STOP)) { + XdrvMailbox.payload = POWER_SHOW_STATE; + } +// Settings.flag.device_index_enable = XdrvMailbox.usridx; ExecuteCommandPower(XdrvMailbox.index, XdrvMailbox.payload, SRC_IGNORE); mqtt_data[0] = '\0'; } + else if (0 == XdrvMailbox.index) { + if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_TOGGLE)) { + XdrvMailbox.payload = POWER_SHOW_STATE; + } + SetAllPower(XdrvMailbox.payload, SRC_IGNORE); + mqtt_data[0] = '\0'; + } } void CmndStatus(void) @@ -607,26 +616,23 @@ void CmndSetoption(void) #endif // USE_HOME_ASSISTANT } } - else if (1 == ptype) { // SetOption50 .. 81 + else if (1 == ptype) { // SetOption50 .. 81 if (XdrvMailbox.payload <= 1) { bitWrite(Settings.flag3.data, pindex, XdrvMailbox.payload); - if (5 == pindex) { // SetOption55 + if (5 == pindex) { // SetOption55 if (0 == XdrvMailbox.payload) { restart_flag = 2; // Disable mDNS needs restart } } - if (10 == pindex) { // SetOption60 enable or disable traditional sleep + if (10 == pindex) { // SetOption60 enable or disable traditional sleep WiFiSetSleepMode(); // Update WiFi sleep mode accordingly } - if (18 == pindex) { // SetOption68 for multi-channel PWM, requires a reboot - restart_flag = 2; - } - if (15 == pindex) { // SetOption65 for tuya_disable_dimmer requires a reboot + if (18 == pindex) { // SetOption68 for multi-channel PWM, requires a reboot restart_flag = 2; } } } - else { // SetOption32 .. 49 + else { // SetOption32 .. 49 uint32_t param_low = 0; uint32_t param_high = 255; switch (pindex) { @@ -635,9 +641,6 @@ void CmndSetoption(void) param_low = 1; param_high = 250; break; - case P_TUYA_RELAYS: - param_high = 8; - break; } if ((XdrvMailbox.payload >= param_low) && (XdrvMailbox.payload <= param_high)) { Settings.param[pindex] = XdrvMailbox.payload; @@ -652,11 +655,7 @@ void CmndSetoption(void) IrReceiveUpdateThreshold(); break; #endif -#ifdef USE_TUYA_DIMMER - case P_TUYA_RELAYS: - case P_TUYA_POWER_ID: - case P_TUYA_CURRENT_ID: - case P_TUYA_VOLTAGE_ID: +#ifdef USE_TUYA_MCU case P_TUYA_DIMMER_MAX: restart_flag = 2; // Need a restart to update GUI break; @@ -1260,6 +1259,10 @@ void CmndReset(void) restart_flag = 210 + XdrvMailbox.payload; Response_P(PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}")); break; + case 99: + Settings.bootcount = 0; + ResponseCmndDone(); + break; default: ResponseCmndChar(D_JSON_ONE_TO_RESET); } @@ -1267,10 +1270,25 @@ void CmndReset(void) void CmndTime(void) { +// payload 0 = (re-)enable NTP +// payload 1 = Time format {"Time":"2019-09-04T14:31:29"} +// payload 2 = Time format {"Time":"2019-09-04T14:31:29","Epoch":1567600289} +// payload 3 = Time format {"Time":1567600289} +// payload 4 = reserved +// payload 1451602800 - disable NTP and set time to epoch + + uint32_t format = Settings.flag2.time_format; if (XdrvMailbox.data_len > 0) { - RtcSetTime(XdrvMailbox.payload); + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) { + Settings.flag2.time_format = XdrvMailbox.payload -1; + format = Settings.flag2.time_format; + } else { + format = 1; // {"Time":"2019-09-04T14:31:29","Epoch":1567600289} + RtcSetTime(XdrvMailbox.payload); + } } - ResponseBeginTime(); + mqtt_data[0] = '\0'; + ResponseAppendTimeFormat(format); ResponseJsonEnd(); } diff --git a/sonoff/support_features.ino b/sonoff/support_features.ino index 42144d191..8265e4cb9 100644 --- a/sonoff/support_features.ino +++ b/sonoff/support_features.ino @@ -171,7 +171,7 @@ void GetFeatures(void) #ifdef USE_PCA9685 feature_drv2 |= 0x00004000; // xdrv_15_pca9685.ino #endif -#if defined(USE_LIGHT) && defined(USE_TUYA_DIMMER) +#if defined(USE_LIGHT) && defined(USE_TUYA_MCU) feature_drv2 |= 0x00008000; // xdrv_16_tuyadimmer.ino #endif #ifdef USE_RC_SWITCH diff --git a/sonoff/support_switch.ino b/sonoff/support_switch.ino index 40bbcc2b9..940fcdb1d 100644 --- a/sonoff/support_switch.ino +++ b/sonoff/support_switch.ino @@ -143,7 +143,7 @@ void SwitchHandler(uint8_t mode) if (Switch.hold_timer[i]) { Switch.hold_timer[i]--; if (0 == Switch.hold_timer[i]) { - SendKey(1, i +1, 3); // Execute command via MQTT + SendKey(KEY_SWITCH, i +1, POWER_HOLD); // Execute command via MQTT } } @@ -152,10 +152,10 @@ void SwitchHandler(uint8_t mode) // enum SwitchModeOptions {TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, PUSHBUTTONHOLD, PUSHBUTTONHOLD_INV, PUSHBUTTON_TOGGLE, MAX_SWITCH_OPTION}; if (button != Switch.last_state[i]) { - switchflag = 3; + switchflag = POWER_TOGGLE +1; switch (Settings.switchmode[i]) { case TOGGLE: - switchflag = 2; // Toggle + switchflag = POWER_TOGGLE; // Toggle break; case FOLLOW: switchflag = button &1; // Follow wall switch state @@ -165,17 +165,17 @@ void SwitchHandler(uint8_t mode) break; case PUSHBUTTON: if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i])) { - switchflag = 2; // Toggle with pushbutton to Gnd + switchflag = POWER_TOGGLE; // Toggle with pushbutton to Gnd } break; case PUSHBUTTON_INV: if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i])) { - switchflag = 2; // Toggle with releasing pushbutton from Gnd + switchflag = POWER_TOGGLE; // Toggle with releasing pushbutton from Gnd } break; case PUSHBUTTON_TOGGLE: if (button != Switch.last_state[i]) { - switchflag = 2; // Toggle with any pushbutton change + switchflag = POWER_TOGGLE; // Toggle with any pushbutton change } break; case PUSHBUTTONHOLD: @@ -184,7 +184,7 @@ void SwitchHandler(uint8_t mode) } if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i]) && (Switch.hold_timer[i])) { Switch.hold_timer[i] = 0; - switchflag = 2; // Toggle with pushbutton to Gnd + switchflag = POWER_TOGGLE; // Toggle with pushbutton to Gnd } break; case PUSHBUTTONHOLD_INV: @@ -193,13 +193,13 @@ void SwitchHandler(uint8_t mode) } if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i]) && (Switch.hold_timer[i])) { Switch.hold_timer[i] = 0; - switchflag = 2; // Toggle with pushbutton to Gnd + switchflag = POWER_TOGGLE; // Toggle with pushbutton to Gnd } break; } - if (switchflag < 3) { - if (!SendKey(1, i +1, switchflag)) { // Execute command via MQTT + if (switchflag <= POWER_TOGGLE) { + if (!SendKey(KEY_SWITCH, i +1, switchflag)) { // Execute command via MQTT ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); // Execute command internally (if i < devices_present) } } diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index c50bef2da..606e40616 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -500,7 +500,7 @@ static bool WifiIsInManagerMode(){ return (HTTP_MANAGER == Web.state || HTTP_MANAGER_RESET_ONLY == Web.state); } -void ShowWebSource(int source) +void ShowWebSource(uint32_t source) { if ((source > 0) && (source < SRC_MAX)) { char stemp1[20]; @@ -508,7 +508,7 @@ void ShowWebSource(int source) } } -void ExecuteWebCommand(char* svalue, int source) +void ExecuteWebCommand(char* svalue, uint32_t source) { ShowWebSource(source); ExecuteCommand(svalue, SRC_IGNORE); @@ -1053,6 +1053,7 @@ bool HandleRootStatusRefresh(void) #ifdef USE_SCRIPT_WEB_DISPLAY XdrvCall(FUNC_WEB_SENSOR); #endif + WSContentSend_P(PSTR("")); if (devices_present) { @@ -1686,8 +1687,8 @@ void HandleBackupConfiguration(void) WSSend(200, CT_STREAM, ""); - uint16_t cfg_crc = Settings.cfg_crc; - Settings.cfg_crc = GetSettingsCrc(); // Calculate crc (again) as it might be wrong when savedata = 0 (#3918) + uint32_t cfg_crc32 = Settings.cfg_crc32; + Settings.cfg_crc32 = GetSettingsCrc32(); // Calculate crc (again) as it might be wrong when savedata = 0 (#3918) memcpy(settings_buffer, &Settings, sizeof(Settings)); if (Web.config_xor_on_set) { @@ -1707,7 +1708,7 @@ void HandleBackupConfiguration(void) SettingsBufferFree(); - Settings.cfg_crc = cfg_crc; // Restore crc in case savedata = 0 to make sure settings will be noted as changed + Settings.cfg_crc32 = cfg_crc32; // Restore crc in case savedata = 0 to make sure settings will be noted as changed } /*-------------------------------------------------------------------------------------------*/ @@ -2092,12 +2093,13 @@ void HandleUploadLoop(void) unsigned long buffer_version = settings_buffer[11] << 24 | settings_buffer[10] << 16 | settings_buffer[9] << 8 | settings_buffer[8]; if (buffer_version > 0x06000000) { uint32_t buffer_size = settings_buffer[3] << 8 | settings_buffer[2]; - uint16_t buffer_crc = settings_buffer[15] << 8 | settings_buffer[14]; - uint16_t crc = 0; - for (uint32_t i = 0; i < buffer_size; i++) { - if ((i < 14) || (i > 15)) { crc += settings_buffer[i]*(i+1); } // Skip crc + if (buffer_version > 0x0606000A) { + uint32_t buffer_crc32 = settings_buffer[4095] << 24 | settings_buffer[4094] << 16 | settings_buffer[4093] << 8 | settings_buffer[4092]; + valid_settings = (GetCfgCrc32(settings_buffer, buffer_size -4) == buffer_crc32); + } else { + uint16_t buffer_crc16 = settings_buffer[15] << 8 | settings_buffer[14]; + valid_settings = (GetCfgCrc16(settings_buffer, buffer_size) == buffer_crc16); } - valid_settings = (buffer_crc == crc); } else { valid_settings = (settings_buffer[0] == CONFIG_FILE_SIGN); } @@ -2350,6 +2352,151 @@ String UrlEncode(const String& text) return encoded; } +#ifdef USE_SENDMAIL + +#include "sendemail.h" + +//SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const bool ssl); +//SendEmail::send(const String& from, const String& to, const String& subject, const String& msg) +// sendmail [server:port:user:passwd:from:to:subject] data +// sendmail [*:*:*:*:*:to:subject] data uses defines from user_config +// sendmail currently only works with core 2.4.2 + +#define SEND_MAIL_MINRAM 19*1024 + +uint16_t SendMail(char *buffer) { + uint16_t count; + char *params,*oparams; + char *mserv; + uint16_t port; + char *user; + char *pstr; + char *passwd; + char *from; + char *to; + char *subject; + char *cmd; + char secure=0,auth=0; + uint16_t status=1; + SendEmail *mail=0; + + //DebugFreeMem(); + +// return if not enough memory + uint16_t mem=ESP.getFreeHeap(); + if (mem AUTH LOGIN 1 => PLAIN LOGIN + // 2 seconds timeout + #define MAIL_TIMEOUT 2000 + mail = new SendEmail(mserv, port,user,passwd, MAIL_TIMEOUT, auth); + +#ifdef EMAIL_FROM + if (*from=='*') { + from=(char*)EMAIL_FROM; + } +#endif + +exit: + if (mail) { + bool result=mail->send(from,to,subject,cmd); + delete mail; + if (result==true) status=0; + } + + + if (oparams) free(oparams); + return status; +} + +#endif + int WebSend(char *buffer) { // [sonoff] POWER1 ON --> Sends http://sonoff/cm?cmnd=POWER1 ON @@ -2462,17 +2609,23 @@ bool JsonWebColor(const char* dataBuf) return true; } -const char kWebSendStatus[] PROGMEM = D_JSON_DONE "|" D_JSON_WRONG_PARAMETERS "|" D_JSON_CONNECT_FAILED "|" D_JSON_HOST_NOT_FOUND ; +const char kWebSendStatus[] PROGMEM = D_JSON_DONE "|" D_JSON_WRONG_PARAMETERS "|" D_JSON_CONNECT_FAILED "|" D_JSON_HOST_NOT_FOUND "|" D_JSON_MEMORY_ERROR; const char kWebCommands[] PROGMEM = "|" // No prefix #ifdef USE_EMULATION D_CMND_EMULATION "|" +#endif +#ifdef USE_SENDMAIL + D_CMND_SENDMAIL "|" #endif D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" D_CMND_WEBSENSOR; void (* const WebCommand[])(void) PROGMEM = { #ifdef USE_EMULATION &CmndEmulation, +#endif +#ifdef USE_SENDMAIL + &CmndSendmail, #endif &CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, &CmndWebSensor }; @@ -2500,6 +2653,18 @@ void CmndEmulation(void) } #endif // USE_EMULATION +#ifdef USE_SENDMAIL +void CmndSendmail(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t result = SendMail(XdrvMailbox.data); + char stemp1[20]; + ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); + } +} +#endif // USE_SENDMAIL + + void CmndWebServer(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino index 10e3eca9c..8de1a3d1e 100644 --- a/sonoff/xdrv_02_mqtt.ino +++ b/sonoff/xdrv_02_mqtt.ino @@ -894,7 +894,7 @@ void CmndButtonRetain(void) if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { if (!XdrvMailbox.payload) { for (uint32_t i = 1; i <= MAX_KEYS; i++) { - SendKey(0, i, 9); // Clear MQTT retain in broker + SendKey(KEY_BUTTON, i, CLEAR_RETAIN); // Clear MQTT retain in broker } } Settings.flag.mqtt_button_retain = XdrvMailbox.payload; @@ -907,7 +907,7 @@ void CmndSwitchRetain(void) if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { if (!XdrvMailbox.payload) { for (uint32_t i = 1; i <= MAX_SWITCHES; i++) { - SendKey(1, i, 9); // Clear MQTT retain in broker + SendKey(KEY_SWITCH, i, CLEAR_RETAIN); // Clear MQTT retain in broker } } Settings.flag.mqtt_switch_retain = XdrvMailbox.payload; diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 13c6c1e97..ef3c98145 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -83,9 +83,9 @@ struct ENERGY { float total = 0; // 12345.12345 kWh tariff 1 + 2 float total1 = 0; // 12345.12345 kWh tariff 1 - off-peak - unsigned long kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only) + unsigned long kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only) + unsigned long kWhtoday_offset = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily - unsigned long kWhtoday1; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily unsigned long period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily uint8_t fifth_second = 0; @@ -129,23 +129,40 @@ void EnergyUpdateToday(void) Energy.kWhtoday_delta -= (delta * 1000); Energy.kWhtoday += delta; } - uint32_t energy_diff = Energy.kWhtoday - RtcSettings.energy_kWhtoday; - RtcSettings.energy_kWhtoday = Energy.kWhtoday; - Energy.daily = (float)Energy.kWhtoday / 100000; - Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday) / 100000; + uint32_t energy_diff = Energy.kWhtoday_offset + Energy.kWhtoday - RtcSettings.energy_kWhtoday; + + RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset + Energy.kWhtoday; + Energy.daily = (float)(RtcSettings.energy_kWhtoday) / 100000; + Energy.total = (float)(RtcSettings.energy_kWhtotal + RtcSettings.energy_kWhtoday) / 100000; if ((RtcTime.hour < Settings.param[P_ENERGY_TARIFF2]) || // Tarrif1 = Off-Peak (RtcTime.hour >= Settings.param[P_ENERGY_TARIFF1]) || (Settings.flag3.energy_weekend && ((RtcTime.day_of_week == 1) || (RtcTime.day_of_week == 7))) ) { - Energy.kWhtoday1 += energy_diff; - RtcSettings.energy_usage.usage1_kWhtoday = Energy.kWhtoday1; - Energy.total1 = (float)(RtcSettings.energy_usage.usage1_kWhtotal + Energy.kWhtoday1) / 100000; + RtcSettings.energy_usage.usage1_kWhtoday += energy_diff; + Energy.total1 = (float)(RtcSettings.energy_usage.usage1_kWhtotal + RtcSettings.energy_usage.usage1_kWhtoday) / 100000; } } +void EnergyUpdateTotal(float value, bool kwh) +{ + char energy_total_chr[FLOATSZ]; + dtostrfd(value, 4, energy_total_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total %s %sWh"), energy_total_chr, (kwh) ? "k" : ""); + + uint32_t multiplier = (kwh) ? 100000 : 100; // kWh or Wh to deca milli Wh + + if (0 == Energy.start_energy || (value < Energy.start_energy)) { + Energy.start_energy = value; // Init after restart and handle roll-over if any + } + else if (value != Energy.start_energy) { + Energy.kWhtoday = (unsigned long)((value - Energy.start_energy) * multiplier); + } + EnergyUpdateToday(); +} + /*********************************************************************************************/ void Energy200ms(void) @@ -160,16 +177,17 @@ void Energy200ms(void) if (RtcTime.valid) { if (LocalTime() == Midnight()) { - Settings.energy_kWhyesterday = Energy.kWhtoday; + Settings.energy_kWhyesterday = RtcSettings.energy_kWhtoday; - Settings.energy_kWhtotal += Energy.kWhtoday; - RtcSettings.energy_kWhtotal = Settings.energy_kWhtotal; + RtcSettings.energy_kWhtotal += RtcSettings.energy_kWhtoday; + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; Energy.kWhtoday = 0; + Energy.kWhtoday_offset = 0; RtcSettings.energy_kWhtoday = 0; + Energy.start_energy = 0; - Settings.energy_usage.usage1_kWhtotal += Energy.kWhtoday1; - RtcSettings.energy_usage.usage1_kWhtotal = Settings.energy_usage.usage1_kWhtotal; - Energy.kWhtoday1 = 0; + RtcSettings.energy_usage.usage1_kWhtotal += RtcSettings.energy_usage.usage1_kWhtoday; + Settings.energy_usage.usage1_kWhtotal = RtcSettings.energy_usage.usage1_kWhtotal; RtcSettings.energy_usage.usage1_kWhtoday = 0; Energy.kWhtoday_delta = 0; @@ -195,12 +213,10 @@ void EnergySaveState(void) { Settings.energy_kWhdoy = (RtcTime.valid) ? RtcTime.day_of_year : 0; - Settings.energy_kWhtoday = Energy.kWhtoday; - RtcSettings.energy_kWhtoday = Energy.kWhtoday; + Settings.energy_kWhtoday = RtcSettings.energy_kWhtoday; Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; - Settings.energy_usage.usage1_kWhtoday = Energy.kWhtoday1; - RtcSettings.energy_usage.usage1_kWhtoday = Energy.kWhtoday1; + Settings.energy_usage.usage1_kWhtoday = RtcSettings.energy_usage.usage1_kWhtoday; Settings.energy_usage.usage1_kWhtotal = RtcSettings.energy_usage.usage1_kWhtotal; } @@ -297,10 +313,10 @@ void EnergyMarginCheck(void) } else { Energy.mplh_counter--; if (!Energy.mplh_counter) { - Response_P(PSTR("{\"" D_JSON_MAXPOWERREACHED "\":\"%d%s\"}"), energy_power_u, (Settings.flag.value_units) ? " " D_UNIT_WATT : ""); + ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHED "\":\"%d%s\"}"), energy_power_u, (Settings.flag.value_units) ? " " D_UNIT_WATT : ""); MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); EnergyMqttShow(); - ExecuteCommandPower(1, POWER_OFF, SRC_MAXPOWER); + SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); if (!Energy.mplr_counter) { Energy.mplr_counter = Settings.param[P_MAX_POWER_RETRY] +1; } @@ -320,11 +336,11 @@ void EnergyMarginCheck(void) if (Energy.mplr_counter) { Energy.mplr_counter--; if (Energy.mplr_counter) { - Response_P(PSTR("{\"" D_JSON_POWERMONITOR "\":\"%s\"}"), GetStateText(1)); + ResponseTime_P(PSTR(",\"" D_JSON_POWERMONITOR "\":\"%s\"}"), GetStateText(1)); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_POWERMONITOR)); - ExecuteCommandPower(1, POWER_ON, SRC_MAXPOWER); + RestorePower(true, SRC_MAXPOWER); } else { - Response_P(PSTR("{\"" D_JSON_MAXPOWERREACHEDRETRY "\":\"%s\"}"), GetStateText(0)); + ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHEDRETRY "\":\"%s\"}"), GetStateText(0)); MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); EnergyMqttShow(); } @@ -338,17 +354,17 @@ void EnergyMarginCheck(void) energy_daily_u = (uint16_t)(Energy.daily * 1000); if (!Energy.max_energy_state && (RtcTime.hour == Settings.energy_max_energy_start)) { Energy.max_energy_state = 1; - Response_P(PSTR("{\"" D_JSON_ENERGYMONITOR "\":\"%s\"}"), GetStateText(1)); + ResponseTime_P(PSTR(",\"" D_JSON_ENERGYMONITOR "\":\"%s\"}"), GetStateText(1)); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_ENERGYMONITOR)); - ExecuteCommandPower(1, POWER_ON, SRC_MAXENERGY); + RestorePower(true, SRC_MAXENERGY); } else if ((1 == Energy.max_energy_state ) && (energy_daily_u >= Settings.energy_max_energy)) { Energy.max_energy_state = 2; dtostrfd(Energy.daily, 3, mqtt_data); - Response_P(PSTR("{\"" D_JSON_MAXENERGYREACHED "\":\"%s%s\"}"), mqtt_data, (Settings.flag.value_units) ? " " D_UNIT_KILOWATTHOUR : ""); + ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":\"%s%s\"}"), mqtt_data, (Settings.flag.value_units) ? " " D_UNIT_KILOWATTHOUR : ""); MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); EnergyMqttShow(); - ExecuteCommandPower(1, POWER_OFF, SRC_MAXENERGY); + SetAllPower(POWER_ALL_OFF, SRC_MAXENERGY); } } #endif // USE_ENERGY_POWER_LIMIT @@ -359,9 +375,10 @@ void EnergyMarginCheck(void) void EnergyMqttShow(void) { // {"Time":"2017-12-16T11:48:55","ENERGY":{"Total":0.212,"Yesterday":0.000,"Today":0.014,"Period":2.0,"Power":22.0,"Factor":1.00,"Voltage":213.6,"Current":0.100}} - ResponseBeginTime(); int tele_period_save = tele_period; tele_period = 2; + mqtt_data[0] = '\0'; + ResponseAppendTime(); EnergyShow(true); tele_period = tele_period_save; ResponseJsonEnd(); @@ -422,13 +439,14 @@ void CmndEnergyReset(void) if (p != XdrvMailbox.data) { switch (XdrvMailbox.index) { case 1: - Energy.kWhtoday = lnum *100; + Energy.kWhtoday_offset = lnum *100; + Energy.kWhtoday = 0; Energy.kWhtoday_delta = 0; - Energy.period = Energy.kWhtoday; - Settings.energy_kWhtoday = Energy.kWhtoday; - RtcSettings.energy_kWhtoday = Energy.kWhtoday; - Energy.daily = (float)Energy.kWhtoday / 100000; - if (!RtcSettings.energy_kWhtotal && !Energy.kWhtoday) { + Energy.period = Energy.kWhtoday_offset; + Settings.energy_kWhtoday = Energy.kWhtoday_offset; + RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset; + Energy.daily = (float)Energy.kWhtoday_offset / 100000; + if (!RtcSettings.energy_kWhtotal && !Energy.kWhtoday_offset) { Settings.energy_kWhtotal_time = LocalTime(); } break; @@ -438,14 +456,14 @@ void CmndEnergyReset(void) case 3: RtcSettings.energy_kWhtotal = lnum *100; Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; - Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday) / 100000; - Settings.energy_kWhtotal_time = (!Energy.kWhtoday) ? LocalTime() : Midnight(); + Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; + Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); break; } } - if (Energy.kWhtoday1 > Energy.kWhtoday) { - Energy.kWhtoday1 = Energy.kWhtoday; + if (RtcSettings.energy_usage.usage1_kWhtoday > (Energy.kWhtoday_offset + Energy.kWhtoday)) { + RtcSettings.energy_usage.usage1_kWhtoday = Energy.kWhtoday_offset + Energy.kWhtoday; } if (Settings.energy_usage.usage1_kWhtoday > Settings.energy_kWhtoday) { Settings.energy_usage.usage1_kWhtoday = Settings.energy_kWhtoday; @@ -686,10 +704,20 @@ void EnergySnsInit(void) XnrgCall(FUNC_INIT); if (energy_flg) { - Energy.kWhtoday = (RtcSettingsValid()) ? RtcSettings.energy_kWhtoday : (RtcTime.day_of_year == Settings.energy_kWhdoy) ? Settings.energy_kWhtoday : 0; - Energy.kWhtoday1 = (RtcSettingsValid()) ? RtcSettings.energy_usage.usage1_kWhtoday : (RtcTime.day_of_year == Settings.energy_kWhdoy) ? Settings.energy_usage.usage1_kWhtoday : 0; + if (RtcSettingsValid()) { + Energy.kWhtoday_offset = RtcSettings.energy_kWhtoday; + } + else if (RtcTime.day_of_year == Settings.energy_kWhdoy) { + Energy.kWhtoday_offset = Settings.energy_kWhtoday; + RtcSettings.energy_usage.usage1_kWhtoday = Settings.energy_usage.usage1_kWhtoday; + } + else { + Energy.kWhtoday_offset = 0; + RtcSettings.energy_usage.usage1_kWhtoday = 0; + } + Energy.kWhtoday = 0; Energy.kWhtoday_delta = 0; - Energy.period = Energy.kWhtoday; + Energy.period = Energy.kWhtoday_offset; EnergyUpdateToday(); ticker_energy.attach_ms(200, Energy200ms); } @@ -770,8 +798,8 @@ void EnergyShow(bool json) float energy = 0; char energy_period_chr[FLOATSZ]; if (show_energy_period) { - if (Energy.period) energy = (float)(Energy.kWhtoday - Energy.period) / 100; - Energy.period = Energy.kWhtoday; + if (Energy.period) energy = (float)(RtcSettings.energy_kWhtoday - Energy.period) / 100; + Energy.period = RtcSettings.energy_kWhtoday; dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr); snprintf_P(speriod, sizeof(speriod), PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); } @@ -806,7 +834,7 @@ void EnergyShow(bool json) char energy_total1_chr[FLOATSZ]; dtostrfd(Energy.total1 * 1000, 1, energy_total1_chr); // Tariff1 char energy_non[2] = "0"; - DomoticzSensorP1SmartMeter(energy_total1_chr, energy_total_chr, energy_non, energy_non, (int)Energy.active_power, 0); + DomoticzSensorP1SmartMeter(energy_total1_chr, energy_total_chr, energy_non, energy_non, (int)Energy.active_power); if (Energy.voltage_available) { DomoticzSensor(DZ_VOLTAGE, voltage_chr); // Voltage @@ -870,6 +898,9 @@ bool Xdrv03(uint8_t function) case FUNC_LOOP: XnrgCall(FUNC_LOOP); break; + case FUNC_EVERY_250_MSECOND: + XnrgCall(FUNC_EVERY_250_MSECOND); + break; #ifdef USE_ENERGY_MARGIN_DETECTION case FUNC_SET_POWER: Energy.power_steady_counter = 2; diff --git a/sonoff/xdrv_05_irremote.ino b/sonoff/xdrv_05_irremote.ino index 59b152a32..cc0614264 100644 --- a/sonoff/xdrv_05_irremote.ino +++ b/sonoff/xdrv_05_irremote.ino @@ -154,7 +154,7 @@ void IrReceiveCheck(void) } else { snprintf_P(svalue, sizeof(svalue), PSTR("\"0x%s\""), hvalue); } - Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d,\"" D_JSON_IR_DATA "\":%s"), + ResponseTime_P(PSTR(",\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d,\"" D_JSON_IR_DATA "\":%s"), GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits, svalue); if (Settings.flag3.receive_raw) { diff --git a/sonoff/xdrv_05_irremote_full.ino b/sonoff/xdrv_05_irremote_full.ino index 4d64e31e4..3c82bee21 100644 --- a/sonoff/xdrv_05_irremote_full.ino +++ b/sonoff/xdrv_05_irremote_full.ino @@ -220,7 +220,7 @@ void IrReceiveCheck(void) // if ((now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) && (UNKNOWN != results.decode_type) && (results.bits > 0)) { if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { ir_lasttime = now; - Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":%s"), sendIRJsonState(results).c_str()); + ResponseTime_P(PSTR(",\"" D_JSON_IRRECEIVED "\":%s"), sendIRJsonState(results).c_str()); if (Settings.flag3.receive_raw) { ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); diff --git a/sonoff/xdrv_06_snfbridge.ino b/sonoff/xdrv_06_snfbridge.ino index c7b57c0c1..67ca582fb 100644 --- a/sonoff/xdrv_06_snfbridge.ino +++ b/sonoff/xdrv_06_snfbridge.ino @@ -214,7 +214,7 @@ void SonoffBridgeReceivedRaw(void) if (0xB1 == serial_in_buffer[1]) { buckets = serial_in_buffer[2] << 1; } // Bucket sniffing - Response_P(PSTR("{\"" D_CMND_RFRAW "\":{\"" D_JSON_DATA "\":\"")); + ResponseTime_P(PSTR(",\"" D_CMND_RFRAW "\":{\"" D_JSON_DATA "\":\"")); for (uint32_t i = 0; i < serial_in_byte_counter; i++) { ResponseAppend_P(PSTR("%02X"), serial_in_buffer[i]); if (0xB1 == serial_in_buffer[1]) { @@ -226,6 +226,7 @@ void SonoffBridgeReceivedRaw(void) } ResponseAppend_P(PSTR("\"}}")); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_CMND_RFRAW)); + XdrvRulesProcess(); } @@ -294,7 +295,7 @@ void SonoffBridgeReceived(void) } else { snprintf_P(stemp, sizeof(stemp), PSTR("\"%06X\""), received_id); } - Response_P(PSTR("{\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":%s,\"" D_CMND_RFKEY "\":%s}}"), + ResponseTime_P(PSTR(",\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":%s,\"" D_CMND_RFKEY "\":%s}}"), sync_time, low_time, high_time, stemp, rfkey); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); XdrvRulesProcess(); diff --git a/sonoff/xdrv_07_domoticz.ino b/sonoff/xdrv_07_domoticz.ino index e5bd2705f..a65c82851 100644 --- a/sonoff/xdrv_07_domoticz.ino +++ b/sonoff/xdrv_07_domoticz.ino @@ -306,7 +306,7 @@ bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg if (device <= MAX_DOMOTICZ_IDX) { if ((Settings.domoticz_key_idx[device -1] || Settings.domoticz_switch_idx[device -1]) && (svalflg)) { Response_P(PSTR("{\"command\":\"switchlight\",\"idx\":%d,\"switchcmd\":\"%s\"}"), - (key) ? Settings.domoticz_switch_idx[device -1] : Settings.domoticz_key_idx[device -1], (state) ? (2 == state) ? "Toggle" : "On" : "Off"); + (key) ? Settings.domoticz_switch_idx[device -1] : Settings.domoticz_key_idx[device -1], (state) ? (POWER_TOGGLE == state) ? "Toggle" : "On" : "Off"); MqttPublish(domoticz_in_topic); result = true; } @@ -383,14 +383,19 @@ void DomoticzSensorPowerEnergy(int power, char *energy) DomoticzSensor(DZ_POWER_ENERGY, data); } -void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int consumed, int produced) +void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int power) { //usage1 = energy usage meter tariff 1, This is an incrementing counter //usage2 = energy usage meter tariff 2, This is an incrementing counter //return1 = energy return meter tariff 1, This is an incrementing counter //return2 = energy return meter tariff 2, This is an incrementing counter - //consumed = actual usage power (Watt) - //produced = actual return power (Watt) + //power = if >= 0 actual usage power. if < 0 actual return power (Watt) + int consumed = power; + int produced = 0; + if (power < 0) { + consumed = 0; + produced = -power; + } char data[64]; snprintf_P(data, sizeof(data), PSTR("%s;%s;%s;%s;%d;%d"), usage1, usage2, return1, return2, consumed, produced); DomoticzSensor(DZ_P1_SMART_METER, data); diff --git a/sonoff/xdrv_08_serial_bridge.ino b/sonoff/xdrv_08_serial_bridge.ino index 98bb6a830..8a0543858 100644 --- a/sonoff/xdrv_08_serial_bridge.ino +++ b/sonoff/xdrv_08_serial_bridge.ino @@ -71,7 +71,7 @@ void SerialBridgeInput(void) if (serial_bridge_in_byte_counter && (millis() > (serial_bridge_polling_window + SERIAL_POLLING))) { serial_bridge_buffer[serial_bridge_in_byte_counter] = 0; // Serial data completed char hex_char[(serial_bridge_in_byte_counter * 2) + 2]; - Response_P(PSTR("{\"" D_JSON_SSERIALRECEIVED "\":\"%s\"}"), + ResponseTime_P(PSTR(",\"" D_JSON_SSERIALRECEIVED "\":\"%s\"}"), (serial_bridge_raw) ? ToHex_P((unsigned char*)serial_bridge_buffer, serial_bridge_in_byte_counter, hex_char, sizeof(hex_char)) : serial_bridge_buffer); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); XdrvRulesProcess(); diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index 674cbfb9b..4b767e77c 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -296,7 +296,7 @@ void TimerEverySecond(void) if (xtimer.days & days) { Settings.timer[i].arm = xtimer.repeat; #if defined(USE_RULES) || defined(USE_SCRIPT) - if (3 == xtimer.power) { // Blink becomes Rule disregarding device and allowing use of Backlog commands + if (POWER_BLINK == xtimer.power) { // Blink becomes Rule disregarding device and allowing use of Backlog commands Response_P(PSTR("{\"Clock\":{\"Timer\":%d}}"), i +1); XdrvRulesProcess(); } else diff --git a/sonoff/xdrv_11_knx.ino b/sonoff/xdrv_11_knx.ino index 73e388fbc..94b320c86 100644 --- a/sonoff/xdrv_11_knx.ino +++ b/sonoff/xdrv_11_knx.ino @@ -575,7 +575,7 @@ void KNX_CB_Action(message_t const &msg, void *arg) else if (chan->type < 17) // Toggle Relays { if (!toggle_inhibit) { - ExecuteCommandPower((chan->type) -8, 2, SRC_KNX); + ExecuteCommandPower((chan->type) -8, POWER_TOGGLE, SRC_KNX); if (Settings.flag.knx_enable_enhancement) { toggle_inhibit = TOGGLE_INHIBIT_TIME; } diff --git a/sonoff/xdrv_13_display.ino b/sonoff/xdrv_13_display.ino index 6a0153545..3a144b6c9 100755 --- a/sonoff/xdrv_13_display.ino +++ b/sonoff/xdrv_13_display.ino @@ -1247,6 +1247,12 @@ void DisplayInitDriver(void) { XdspCall(FUNC_DISPLAY_INIT_DRIVER); + if (renderer) { + renderer->setTextFont(Settings.display_font); + renderer->setTextSize(Settings.display_size); + } + + // AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Display model %d"), Settings.display_model); if (Settings.display_model) { @@ -1376,14 +1382,18 @@ void CmndDisplaySize(void) { if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) { Settings.display_size = XdrvMailbox.payload; + if (renderer) renderer->setTextSize(Settings.display_size); + else DisplaySetSize(Settings.display_size); } ResponseCmndNumber(Settings.display_size); } void CmndDisplayFont(void) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) { + if ((XdrvMailbox.payload >=0) && (XdrvMailbox.payload <= 4)) { Settings.display_font = XdrvMailbox.payload; + if (renderer) renderer->setTextFont(Settings.display_font); + else DisplaySetFont(Settings.display_font); } ResponseCmndNumber(Settings.display_font); } @@ -1804,7 +1814,7 @@ void Restore_graph(uint8_t num, char *path) { if (!fp) return; char vbuff[32]; char *cp=vbuff; - char buf[2]; + uint8_t buf[2]; uint8_t findex=0; for (uint32_t count=0;count<=gp->xs+4;count++) { diff --git a/sonoff/xdrv_15_pca9685.ino b/sonoff/xdrv_15_pca9685.ino index 906207851..fb1a923d5 100644 --- a/sonoff/xdrv_15_pca9685.ino +++ b/sonoff/xdrv_15_pca9685.ino @@ -166,8 +166,7 @@ bool PCA9685_Command(void) void PCA9685_OutputTelemetry(bool telemetry) { if (0 == pca9685_detected) { return; } // We do not do this if the PCA9685 has not been detected - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"PCA9685\":{\"PWM_FREQ\":%i,"),pca9685_freq); + ResponseTime_P(PSTR(",\"PCA9685\":{\"PWM_FREQ\":%i,"),pca9685_freq); for (uint32_t pin=0;pin<16;pin++) { ResponseAppend_P(PSTR("\"PWM%i\":%i,"),pin,pca9685_pin_pwm_value[pin]); } diff --git a/sonoff/xdrv_16_tuyadimmer.ino b/sonoff/xdrv_16_tuyamcu.ino similarity index 58% rename from sonoff/xdrv_16_tuyadimmer.ino rename to sonoff/xdrv_16_tuyamcu.ino index 608236f4b..48e97334a 100644 --- a/sonoff/xdrv_16_tuyadimmer.ino +++ b/sonoff/xdrv_16_tuyamcu.ino @@ -1,5 +1,5 @@ /* - xdrv_16_tuyadimmer.ino - Tuya dimmer support for Sonoff-Tasmota + xdrv_16_tuyamcu.ino - Tuya MCU support for Sonoff-Tasmota Copyright (C) 2019 digiblur, Joel Stein and Theo Arends @@ -18,7 +18,7 @@ */ #ifdef USE_LIGHT -#ifdef USE_TUYA_DIMMER +#ifdef USE_TUYA_MCU #define XDRV_16 16 #define XNRG_08 8 @@ -61,10 +61,156 @@ struct TUYA { int byte_counter = 0; // Index in serial receive buffer } Tuya; + +enum TuyaSupportedFunctions { + TUYA_MCU_FUNC_NONE, + TUYA_MCU_FUNC_SWT1 = 1, // Buttons + TUYA_MCU_FUNC_SWT2, + TUYA_MCU_FUNC_SWT3, + TUYA_MCU_FUNC_SWT4, + TUYA_MCU_FUNC_REL1 = 11, // Relays + TUYA_MCU_FUNC_REL2, + TUYA_MCU_FUNC_REL3, + TUYA_MCU_FUNC_REL4, + TUYA_MCU_FUNC_REL5, + TUYA_MCU_FUNC_REL6, + TUYA_MCU_FUNC_REL7, + TUYA_MCU_FUNC_REL8, + TUYA_MCU_FUNC_DIMMER = 21, + TUYA_MCU_FUNC_POWER = 31, + TUYA_MCU_FUNC_CURRENT, + TUYA_MCU_FUNC_VOLTAGE, + TUYA_MCU_FUNC_REL1_INV = 41, // Inverted Relays + TUYA_MCU_FUNC_REL2_INV, + TUYA_MCU_FUNC_REL3_INV, + TUYA_MCU_FUNC_REL4_INV, + TUYA_MCU_FUNC_REL5_INV, + TUYA_MCU_FUNC_REL6_INV, + TUYA_MCU_FUNC_REL7_INV, + TUYA_MCU_FUNC_REL8_INV, + TUYA_MCU_FUNC_LAST = 255 +}; + +const char kTuyaCommand[] PROGMEM = "|" // No prefix + D_CMND_TUYA_MCU; + +void (* const TuyaCommand[])(void) PROGMEM = { + &CmndTuyaMcu +}; + + +/* + +TuyaMcu fnid,dpid + +*/ + +void CmndTuyaMcu(void) { + if (XdrvMailbox.data_len > 0) { + char *p; + uint8_t i = 0; + uint8_t parm[3] = { 0 }; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) { + parm[i] = strtoul(str, nullptr, 0); + i++; + } + + if (TuyaFuncIdValid(parm[0])) { + TuyaAddMcuFunc(parm[0], parm[1]); + restart_flag = 2; + } else { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("TYA: TuyaMcu Invalid function id=%d"), parm[0]); + } + + } + + Response_P(PSTR("[")); + bool added = false; + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].fnid != 0) { + if (added) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"fnId\":%d, \"dpId\":%d}" ), Settings.tuya_fnid_map[i].fnid, Settings.tuya_fnid_map[i].dpid); + added = true; + } + } + ResponseAppend_P(PSTR("]")); +} + /*********************************************************************************************\ * Internal Functions \*********************************************************************************************/ +void TuyaAddMcuFunc(uint8_t fnId, uint8_t dpId) { + bool added = false; + + if (fnId == 0 || dpId == 0) { // Delete entry + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if ((dpId > 0 && Settings.tuya_fnid_map[i].dpid == dpId) || (fnId > TUYA_MCU_FUNC_NONE && Settings.tuya_fnid_map[i].fnid == fnId)) { + Settings.tuya_fnid_map[i].fnid = TUYA_MCU_FUNC_NONE; + Settings.tuya_fnid_map[i].dpid = 0; + break; + } + } + } else { // Add or update + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].dpid == dpId || Settings.tuya_fnid_map[i].dpid == 0 || Settings.tuya_fnid_map[i].fnid == fnId || Settings.tuya_fnid_map[i].fnid == 0) { + if (!added) { // Update entry if exisiting entry or add + Settings.tuya_fnid_map[i].fnid = fnId; + Settings.tuya_fnid_map[i].dpid = dpId; + added = true; + } else if (Settings.tuya_fnid_map[i].dpid == dpId || Settings.tuya_fnid_map[i].fnid == fnId) { // Remove existing entry if added to empty place + Settings.tuya_fnid_map[i].fnid = TUYA_MCU_FUNC_NONE; + Settings.tuya_fnid_map[i].dpid = 0; + } + } + } + } + UpdateDevices(); +} + +void UpdateDevices() { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + uint8_t fnId = Settings.tuya_fnid_map[i].fnid; + if (fnId > TUYA_MCU_FUNC_NONE && Settings.tuya_fnid_map[i].dpid > 0) { + + if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) { //Relay + bitClear(rel_inverted, fnId - TUYA_MCU_FUNC_REL1); + } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { // Inverted Relay + bitSet(rel_inverted, fnId - TUYA_MCU_FUNC_REL1_INV); + } + + } + } +} + +inline bool TuyaFuncIdValid(uint8_t fnId) { + return (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) || + (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) || + fnId == TUYA_MCU_FUNC_DIMMER || + (fnId >= TUYA_MCU_FUNC_POWER && fnId <= TUYA_MCU_FUNC_VOLTAGE) || + (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV); +} + +uint8_t TuyaGetFuncId(uint8_t dpid) { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].dpid == dpid) { + return Settings.tuya_fnid_map[i].fnid; + } + } + return TUYA_MCU_FUNC_NONE; +} + +uint8_t TuyaGetDpId(uint8_t fnId) { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].fnid == fnId) { + return Settings.tuya_fnid_map[i].dpid; + } + } + return 0; +} + void TuyaSendCmd(uint8_t cmd, uint8_t payload[] = nullptr, uint16_t payload_len = 0) { uint8_t checksum = (0xFF + cmd + (payload_len >> 8) + (payload_len & 0xFF)); @@ -131,7 +277,7 @@ bool TuyaSetPower(void) int16_t source = XdrvMailbox.payload; if (source != SRC_SWITCH && TuyaSerial) { // ignore to prevent loop from pushing state from faceplate interaction - TuyaSendBool(active_device, bitRead(rpower, active_device-1)); + TuyaSendBool(active_device, bitRead(rpower, active_device-1) ^ bitRead(rel_inverted, active_device-1)); status = true; } return status; @@ -146,24 +292,22 @@ bool TuyaSetChannels(void) void LightSerialDuty(uint8_t duty) { - if (duty > 0 && !Tuya.ignore_dim && TuyaSerial) { + uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER); + if (duty > 0 && !Tuya.ignore_dim && TuyaSerial && dpid > 0) { if (Settings.flag3.tuya_dimmer_min_limit) { // Enable dimming limit SetOption69: Enabled by default if (duty < 25) { duty = 25; } // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself } - - if (Settings.flag3.tuya_disable_dimmer == 0) { - duty = changeUIntScale(duty, 0, 255, 0, Settings.param[P_TUYA_DIMMER_MAX]); - if (Tuya.new_dim != duty) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim value=%d (id=%d)"), duty, Settings.param[P_TUYA_DIMMER_ID]); - TuyaSendValue(Settings.param[P_TUYA_DIMMER_ID], duty); - } + duty = changeUIntScale(duty, 0, 255, 0, Settings.param[P_TUYA_DIMMER_MAX]); + if (Tuya.new_dim != duty) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim value=%d (id=%d)"), duty, dpid); + TuyaSendValue(dpid, duty); } - } else { + } else if (dpid > 0) { Tuya.ignore_dim = false; // reset flag - if (Settings.flag3.tuya_disable_dimmer == 0) { - duty = changeUIntScale(duty, 0, 255, 0, Settings.param[P_TUYA_DIMMER_MAX]); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value=%d"), duty); // due to 0 or already set - } + duty = changeUIntScale(duty, 0, 255, 0, Settings.param[P_TUYA_DIMMER_MAX]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value=%d"), duty); // due to 0 or already set + } else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown")); // } } @@ -189,6 +333,7 @@ void TuyaResetWifi(void) void TuyaPacketProcess(void) { char scmnd[20]; + uint8_t fnId = TUYA_MCU_FUNC_NONE; switch (Tuya.buffer[3]) { @@ -201,24 +346,33 @@ void TuyaPacketProcess(void) break; case TUYA_CMD_STATE: - if (Tuya.buffer[5] == 5) { // on/off packet + fnId = TuyaGetFuncId(Tuya.buffer[6]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: FnId=%d is set for dpId=%d"), fnId, Tuya.buffer[6]); + // if (TuyaFuncIdValid(fnId)) { + if (Tuya.buffer[5] == 5) { // on/off packet - /*if ((power || Settings.light_dimmer > 0) && (power != Tuya.buffer[10])) { - ExecuteCommandPower(1, Tuya.buffer[10], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction - }*/ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Device-%d --> MCU State: %s Current State:%s"),Tuya.buffer[6],Tuya.buffer[10]?"On":"Off",bitRead(power, Tuya.buffer[6]-1)?"On":"Off"); - if ((power || Settings.light_dimmer > 0) && (Tuya.buffer[10] != bitRead(power, Tuya.buffer[6]-1))) { - ExecuteCommandPower(Tuya.buffer[6], Tuya.buffer[10], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction - } - } - else if (Tuya.buffer[5] == 8) { // Long value packet + if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[10]?"On":"Off",bitRead(power, fnId - TUYA_MCU_FUNC_REL1)?"On":"Off"); + if ((power || Settings.light_dimmer > 0) && (Tuya.buffer[10] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1))) { + ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[10], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction + } + } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d-Inverted --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[10]?"Off":"On",bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1?"Off":"On"); + if (Tuya.buffer[10] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1) { + ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[10] ^ 1, SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction + } + } else if (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Switch-%d --> MCU State: %d Current State:%d"),fnId - TUYA_MCU_FUNC_SWT1 + 1,Tuya.buffer[10], SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1)); - if (Settings.flag3.tuya_disable_dimmer == 0) { - if (!Settings.param[P_TUYA_DIMMER_ID]) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Autoconfiguring Dimmer ID %d"), Tuya.buffer[6]); - Settings.param[P_TUYA_DIMMER_ID] = Tuya.buffer[6]; + if (SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1) != Tuya.buffer[10]) { + SwitchSetVirtual(fnId - TUYA_MCU_FUNC_SWT1, Tuya.buffer[10]); + SwitchHandler(1); + } } - if (Settings.param[P_TUYA_DIMMER_ID] == Tuya.buffer[6]) { + + } + else if (Tuya.buffer[5] == 8) { // Long value packet + if (fnId == TUYA_MCU_FUNC_DIMMER) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Dim State=%d"), Tuya.buffer[13]); Tuya.new_dim = changeUIntScale((uint8_t) Tuya.buffer[13], 0, Settings.param[P_TUYA_DIMMER_MAX], 0, 100); if ((power || Settings.flag3.tuya_apply_o20) && (Tuya.new_dim > 0) && (abs(Tuya.new_dim - Settings.light_dimmer) > 1)) { @@ -228,30 +382,30 @@ void TuyaPacketProcess(void) ExecuteCommand(scmnd, SRC_SWITCH); } } - } -#ifdef USE_ENERGY_SENSOR - if (Settings.param[P_TUYA_VOLTAGE_ID] == Tuya.buffer[6]) { - Energy.voltage = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Settings.param[P_TUYA_VOLTAGE_ID], (Tuya.buffer[12] << 8 | Tuya.buffer[13])); - } else if (Settings.param[P_TUYA_CURRENT_ID] == Tuya.buffer[6]) { - Energy.current = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 1000; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Settings.param[P_TUYA_CURRENT_ID], (Tuya.buffer[12] << 8 | Tuya.buffer[13])); - } else if (Settings.param[P_TUYA_POWER_ID] == Tuya.buffer[6]) { - Energy.active_power = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Settings.param[P_TUYA_POWER_ID], (Tuya.buffer[12] << 8 | Tuya.buffer[13])); + #ifdef USE_ENERGY_SENSOR + else if (fnId == TUYA_MCU_FUNC_VOLTAGE) { + Energy.voltage = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[6], (Tuya.buffer[12] << 8 | Tuya.buffer[13])); + } else if (fnId == TUYA_MCU_FUNC_CURRENT) { + Energy.current = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 1000; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[6], (Tuya.buffer[12] << 8 | Tuya.buffer[13])); + } else if (fnId == TUYA_MCU_FUNC_POWER) { + Energy.active_power = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[6], (Tuya.buffer[12] << 8 | Tuya.buffer[13])); - if (Tuya.lastPowerCheckTime != 0 && Energy.active_power > 0) { - Energy.kWhtoday += (float)Energy.active_power * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36; - EnergyUpdateToday(); + if (Tuya.lastPowerCheckTime != 0 && Energy.active_power > 0) { + Energy.kWhtoday += (float)Energy.active_power * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36; + EnergyUpdateToday(); + } + Tuya.lastPowerCheckTime = Rtc.utc_time; } - Tuya.lastPowerCheckTime = Rtc.utc_time; - } else if (Settings.param[P_TUYA_DIMMER_ID] != Tuya.buffer[6]){ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Unknown ID=%d"), Tuya.buffer[6]); - } -#endif // USE_ENERGY_SENSOR + #endif // USE_ENERGY_SENSOR - } + } + // } else { + // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Unknown FnId=%s for dpId=%s"), fnId, Tuya.buffer[6]); + // } break; case TUYA_CMD_WIFI_RESET: @@ -266,9 +420,9 @@ void TuyaPacketProcess(void) break; case TUYA_CMD_MCU_CONF: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX MCU configuration")); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX MCU configuration Mode=%d"), Tuya.buffer[5]); - if (Tuya.buffer[5] == 2) { + if (Tuya.buffer[5] == 2) { // Processing by ESP module mode uint8_t led1_gpio = Tuya.buffer[6]; uint8_t key1_gpio = Tuya.buffer[7]; bool key1_set = false; @@ -307,21 +461,41 @@ bool TuyaModuleSelected(void) Settings.my_gp.io[3] = GPIO_TUYA_RX; restart_flag = 2; } - if (Settings.flag3.tuya_disable_dimmer == 0) { + + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) == 0 && TUYA_DIMMER_ID > 0) { + TuyaAddMcuFunc(TUYA_MCU_FUNC_DIMMER, TUYA_DIMMER_ID); + } + + bool relaySet = false; + + devices_present--; + + for (uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS; i++) { + if ((Settings.tuya_fnid_map[i].fnid >= TUYA_MCU_FUNC_REL1 && Settings.tuya_fnid_map[i].fnid <= TUYA_MCU_FUNC_REL8 ) || + (Settings.tuya_fnid_map[i].fnid >= TUYA_MCU_FUNC_REL1_INV && Settings.tuya_fnid_map[i].fnid <= TUYA_MCU_FUNC_REL8_INV )) { + relaySet = true; + devices_present++; + } + } + + if (!relaySet) { + TuyaAddMcuFunc(TUYA_MCU_FUNC_REL1, 1); + devices_present++; + SettingsSaveAll(); + } + + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) != 0) { light_type = LT_SERIAL1; } else { light_type = LT_BASIC; } + UpdateDevices(); return true; } void TuyaInit(void) { - devices_present += Settings.param[P_TUYA_RELAYS]; // SetOption41 - Add virtual relays if present - if (!Settings.param[P_TUYA_DIMMER_ID]) { - Settings.param[P_TUYA_DIMMER_ID] = TUYA_DIMMER_ID; - } Tuya.buffer = (char*)(malloc(TUYA_BUFFER_SIZE)); if (Tuya.buffer != nullptr) { TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 2); @@ -434,11 +608,11 @@ int Xnrg08(uint8_t function) if (TUYA_DIMMER == my_module_type) { if (FUNC_PRE_INIT == function) { if (!energy_flg) { - if (Settings.param[P_TUYA_POWER_ID] != 0) { - if (Settings.param[P_TUYA_CURRENT_ID] == 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_POWER) != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_CURRENT) == 0) { Energy.current_available = false; } - if (Settings.param[P_TUYA_VOLTAGE_ID] == 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_VOLTAGE) == 0) { Energy.voltage_available = false; } energy_flg = XNRG_08; @@ -486,10 +660,13 @@ bool Xdrv16(uint8_t function) case FUNC_SET_CHANNELS: result = TuyaSetChannels(); break; + case FUNC_COMMAND: + result = DecodeCommand(kTuyaCommand, TuyaCommand); + break; } } return result; } -#endif // USE_TUYA_DIMMER +#endif // USE_TUYA_MCU #endif // USE_LIGHT diff --git a/sonoff/xdrv_17_rcswitch.ino b/sonoff/xdrv_17_rcswitch.ino index 902304f0b..9d6d33660 100644 --- a/sonoff/xdrv_17_rcswitch.ino +++ b/sonoff/xdrv_17_rcswitch.ino @@ -67,7 +67,7 @@ void RfReceiveCheck(void) } else { snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)data); } - Response_P(PSTR("{\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_DATA "\":%s,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_PROTOCOL "\":%d,\"" D_JSON_RF_PULSE "\":%d}}"), + ResponseTime_P(PSTR(",\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_DATA "\":%s,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_PROTOCOL "\":%d,\"" D_JSON_RF_PULSE "\":%d}}"), stemp, bits, protocol, delay); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); XdrvRulesProcess(); diff --git a/sonoff/xdrv_22_sonoff_ifan.ino b/sonoff/xdrv_22_sonoff_ifan.ino index b2d9838f8..0dbb31d50 100644 --- a/sonoff/xdrv_22_sonoff_ifan.ino +++ b/sonoff/xdrv_22_sonoff_ifan.ino @@ -96,7 +96,7 @@ void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence) fans = kIFan03Speed[fanspeed]; } for (uint32_t i = 2; i < 5; i++) { - uint8_t state = (fans &1) + 6; // Add no publishPowerState + uint8_t state = (fans &1) + POWER_OFF_NO_STATE; // Add no publishPowerState ExecuteCommandPower(i, state, SRC_IGNORE); // Use relay 2, 3 and 4 fans >>= 1; } diff --git a/sonoff/xdrv_23_zigbee_impl.ino b/sonoff/xdrv_23_zigbee_impl.ino index 1f44a7ab5..1653ed78c 100644 --- a/sonoff/xdrv_23_zigbee_impl.ino +++ b/sonoff/xdrv_23_zigbee_impl.ino @@ -156,10 +156,10 @@ public: void publishMQTTReceived(void) { char hex_char[_payload.len()*2+2]; ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); - Response_P(PSTR("{\"" D_JSON_ZIGBEEZCLRECEIVED "\":{\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," - "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), - _frame_control, _manuf_code, _transact_seq, _cmd_id, - hex_char); + ResponseTime_P(PSTR(",\"" D_JSON_ZIGBEEZCLRECEIVED "\":{\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," + "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), + _frame_control, _manuf_code, _transact_seq, _cmd_id, + hex_char); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCLSENT)); XdrvRulesProcess(); } @@ -834,7 +834,7 @@ void ZigbeeInput(void) SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); // remove SOF, LEN and FCS ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char)); - Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char); + ResponseTime_P(PSTR(",\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZNPRECEIVED)); XdrvRulesProcess(); diff --git a/sonoff/xdsp_07_sh1106.ino b/sonoff/xdsp_07_sh1106.ino index 1c45a451d..0501233a7 100644 --- a/sonoff/xdsp_07_sh1106.ino +++ b/sonoff/xdsp_07_sh1106.ino @@ -145,13 +145,13 @@ void SH1106Refresh(void) // Every second if (Settings.display_mode) { // Mode 0 is User text switch (Settings.display_mode) { case 1: // Time - Ssd1306Time(); + SH1106Time(); break; case 2: // Local case 3: // Local case 4: // Mqtt case 5: // Mqtt - Ssd1306PrintLog(); + SH1106PrintLog(); break; } } diff --git a/sonoff/xdsp_08_ILI9488.ino b/sonoff/xdsp_08_ILI9488.ino index 51cb0fd4c..071280601 100644 --- a/sonoff/xdsp_08_ILI9488.ino +++ b/sonoff/xdsp_08_ILI9488.ino @@ -124,13 +124,19 @@ void ILI9488_InitDriver() #ifdef USE_TOUCH_BUTTONS void ILI9488_MQTT(uint8_t count,const char *cp) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"RA8876\":{\"%s%d\":\"%d\"}}"), mqtt_data,cp,count+1,(buttons[count]->vpower&0x80)>>7); + ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); } + +void ILI9488_RDW_BUTT(uint32_t count,uint32_t pwr) { + buttons[count]->xdrawButton(pwr); + if (pwr) buttons[count]->vpower|=0x80; + else buttons[count]->vpower&=0x7f; +} // check digitizer hit void FT6236Check() { uint16_t temp; +uint8_t rbutt=0,vbutt=0; ili9488_ctouch_counter++; if (2 == ili9488_ctouch_counter) { // every 100 ms should be enough @@ -158,17 +164,17 @@ if (2 == ili9488_ctouch_counter) { // now must compare with defined buttons for (uint8_t count=0; countvpower&0x7f; if (buttons[count]->contains(ili9488_pLoc.x,ili9488_pLoc.y)) { // did hit buttons[count]->press(true); if (buttons[count]->justPressed()) { - uint8_t bflags=buttons[count]->vpower&0x7f; if (!bflags) { - // real button - if (!SendKey(0, count+1, POWER_TOGGLE)) { - ExecuteCommandPower(count+1, POWER_TOGGLE, SRC_BUTTON); + uint8_t pwr=bitRead(power,rbutt); + if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) { + ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON); + ILI9488_RDW_BUTT(count,!pwr); } - buttons[count]->xdrawButton(bitRead(power,count)); } else { // virtual button const char *cp; @@ -186,6 +192,11 @@ if (2 == ili9488_ctouch_counter) { } } } + if (!bflags) { + rbutt++; + } else { + vbutt++; + } } } } @@ -193,15 +204,27 @@ if (2 == ili9488_ctouch_counter) { // no hit for (uint8_t count=0; countvpower&0x7f; buttons[count]->press(false); if (buttons[count]->justReleased()) { uint8_t bflags=buttons[count]->vpower&0x7f; - if (bflags>1) { - // push button - buttons[count]->vpower&=0x7f; - ILI9488_MQTT(count,"PBT"); + if (bflags>0) { + if (bflags>1) { + // push button + buttons[count]->vpower&=0x7f; + ILI9488_MQTT(count,"PBT"); + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); } - buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + } + if (!bflags) { + // check if power button stage changed + uint8_t pwr=bitRead(power,rbutt); + uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; + if (pwr!=vpwr) { + ILI9488_RDW_BUTT(count,pwr); + } + rbutt++; } } } diff --git a/sonoff/xdsp_10_RA8876.ino b/sonoff/xdsp_10_RA8876.ino index d77ed6c00..d138ae154 100644 --- a/sonoff/xdsp_10_RA8876.ino +++ b/sonoff/xdsp_10_RA8876.ino @@ -109,14 +109,20 @@ void RA8876_InitDriver() #ifdef USE_TOUCH_BUTTONS void RA8876_MQTT(uint8_t count,const char *cp) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"RA8876\":{\"%s%d\":\"%d\"}}"), mqtt_data,cp,count+1,(buttons[count]->vpower&0x80)>>7); + ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); } +void RA8876_RDW_BUTT(uint32_t count,uint32_t pwr) { + buttons[count]->xdrawButton(pwr); + if (pwr) buttons[count]->vpower|=0x80; + else buttons[count]->vpower&=0x7f; +} + // check digitizer hit void FT5316Check() { uint16_t temp; +uint8_t rbutt=0,vbutt=0; ra8876_ctouch_counter++; if (2 == ra8876_ctouch_counter) { // every 100 ms should be enough @@ -153,23 +159,26 @@ if (2 == ra8876_ctouch_counter) { break; } */ + //AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %d,%d"),ra8876_pLoc.x,ra8876_pLoc.y); + //Serial.printf("loc x: %d , loc y: %d\n",pLoc.x,pLoc.y); // now must compare with defined buttons for (uint8_t count=0; countvpower&0x7f; if (buttons[count]->contains(ra8876_pLoc.x,ra8876_pLoc.y)) { // did hit buttons[count]->press(true); if (buttons[count]->justPressed()) { - uint8_t bflags=buttons[count]->vpower&0x7f; if (!bflags) { // real button - if (!SendKey(0, count+1, POWER_TOGGLE)) { - ExecuteCommandPower(count+1, POWER_TOGGLE, SRC_BUTTON); + uint8_t pwr=bitRead(power,rbutt); + if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) { + ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON); + RA8876_RDW_BUTT(count,!pwr); } - buttons[count]->xdrawButton(bitRead(power,count)); } else { // virtual button const char *cp; @@ -187,6 +196,11 @@ if (2 == ra8876_ctouch_counter) { } } } + if (!bflags) { + rbutt++; + } else { + vbutt++; + } } } } @@ -194,15 +208,26 @@ if (2 == ra8876_ctouch_counter) { // no hit for (uint8_t count=0; countvpower&0x7f; buttons[count]->press(false); if (buttons[count]->justReleased()) { - uint8_t bflags=buttons[count]->vpower&0x7f; - if (bflags>1) { - // push button - buttons[count]->vpower&=0x7f; - RA8876_MQTT(count,"PBT"); + if (bflags>0) { + if (bflags>1) { + // push button + buttons[count]->vpower&=0x7f; + RA8876_MQTT(count,"PBT"); + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); } - buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + } + if (!bflags) { + // check if power button stage changed + uint8_t pwr=bitRead(power,rbutt); + uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; + if (pwr!=vpwr) { + RA8876_RDW_BUTT(count,pwr); + } + rbutt++; } } } diff --git a/sonoff/xnrg_03_pzem004t.ino b/sonoff/xnrg_03_pzem004t.ino index 4083d9b38..b31f29582 100644 --- a/sonoff/xnrg_03_pzem004t.ino +++ b/sonoff/xnrg_03_pzem004t.ino @@ -181,12 +181,7 @@ void PzemEvery200ms(void) Energy.active_power = value; break; case 4: // Total energy as 99999Wh - if (!Energy.start_energy || (value < Energy.start_energy)) Energy.start_energy = value; // Init after restart and hanlde roll-over if any - if (value != Energy.start_energy) { - Energy.kWhtoday += (unsigned long)((value - Energy.start_energy) * 100); - Energy.start_energy = value; - } - EnergyUpdateToday(); + EnergyUpdateTotal(value, false); break; } pzem_read_state++; diff --git a/sonoff/xnrg_05_pzem_ac.ino b/sonoff/xnrg_05_pzem_ac.ino index 79f97e508..55814435a 100644 --- a/sonoff/xnrg_05_pzem_ac.ino +++ b/sonoff/xnrg_05_pzem_ac.ino @@ -64,7 +64,7 @@ void PzemAcEverySecond(void) uint8_t buffer[26]; uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, 10); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "PzemAc response error %d"), error); @@ -82,12 +82,7 @@ void PzemAcEverySecond(void) Energy.power_factor = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 float energy = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh - if (!Energy.start_energy || (energy < Energy.start_energy)) { Energy.start_energy = energy; } // Init after restart and handle roll-over if any - if (energy != Energy.start_energy) { - Energy.kWhtoday += (unsigned long)((energy - Energy.start_energy) * 100); - Energy.start_energy = energy; - } - EnergyUpdateToday(); + EnergyUpdateTotal(energy, false); // } } } diff --git a/sonoff/xnrg_06_pzem_dc.ino b/sonoff/xnrg_06_pzem_dc.ino index 4c8db837d..bba38dd3d 100644 --- a/sonoff/xnrg_06_pzem_dc.ino +++ b/sonoff/xnrg_06_pzem_dc.ino @@ -46,7 +46,7 @@ void PzemDcEverySecond(void) uint8_t buffer[22]; uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, 8); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "PzemDc response error %d"), error); @@ -61,12 +61,7 @@ void PzemDcEverySecond(void) Energy.active_power = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W float energy = (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh - if (!Energy.start_energy || (energy < Energy.start_energy)) { Energy.start_energy = energy; } // Init after restart and handle roll-over if any - if (energy != Energy.start_energy) { - Energy.kWhtoday += (unsigned long)((energy - Energy.start_energy) * 100); - Energy.start_energy = energy; - } - EnergyUpdateToday(); + EnergyUpdateTotal(energy, false); } } diff --git a/sonoff/xnrg_09_sdm120.ino b/sonoff/xnrg_09_sdm120.ino index 34242b2ca..368c64021 100644 --- a/sonoff/xnrg_09_sdm120.ino +++ b/sonoff/xnrg_09_sdm120.ino @@ -78,7 +78,7 @@ struct SDM220 { /*********************************************************************************************/ -void SDM120Every200ms(void) +void SDM120Every250ms(void) { bool data_ready = Sdm120Modbus->ReceiveReady(); @@ -86,15 +86,18 @@ void SDM120Every200ms(void) uint8_t buffer[9]; uint32_t error = Sdm120Modbus->ReceiveBuffer(buffer, 2); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SDM120 response error %d"), error); } else { Energy.data_valid = 0; + // 0 1 2 3 4 5 6 7 8 + // SA FC BC Fh Fl Sh Sl Cl Ch + // 01 04 04 43 66 33 34 1B 38 = 230.2 Volt float value; - ((uint8_t*)&value)[3] = buffer[3]; // Get float values + ((uint8_t*)&value)[3] = buffer[3]; // Get float values ((uint8_t*)&value)[2] = buffer[4]; ((uint8_t*)&value)[1] = buffer[5]; ((uint8_t*)&value)[0] = buffer[6]; @@ -129,14 +132,7 @@ void SDM120Every200ms(void) break; case 7: - if (!Energy.start_energy || (value < Energy.start_energy)) { // 484.708 kWh - Energy.start_energy = value; // Init after restart and hanlde roll-over if any - } - if (value != Energy.start_energy) { - Energy.kWhtoday += (unsigned long)((value - Energy.start_energy) * 100000); // kWh to deca milli Wh - Energy.start_energy = value; - } - EnergyUpdateToday(); + EnergyUpdateTotal(value, true); // 484.708 kWh break; #ifdef USE_SDM220 @@ -266,8 +262,8 @@ int Xnrg09(uint8_t function) case FUNC_INIT: Sdm120SnsInit(); break; - case FUNC_EVERY_200_MSECOND: - if (uptime > 4) { SDM120Every200ms(); } + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { SDM120Every250ms(); } break; #ifdef USE_SDM220 diff --git a/sonoff/xsns_29_mcp230xx.ino b/sonoff/xsns_29_mcp230xx.ino index cd4973458..caefb67e8 100644 --- a/sonoff/xsns_29_mcp230xx.ino +++ b/sonoff/xsns_29_mcp230xx.ino @@ -303,8 +303,7 @@ void MCP230xx_CheckForInterrupt(void) { break; } if (int_tele) { - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}}"), + ResponseTime_P(PSTR(",\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}}"), intp+(mcp230xx_port*8), ((mcp230xx_intcap >> intp) & 0x01),millis_since_last_int); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("MCP230XX_INT")); } @@ -730,8 +729,7 @@ void MCP230xx_OutputTelemetry(void) { } if (outputcount) { char stt[7]; - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"MCP230_OUT\":{")); + ResponseTime_P(PSTR(",\"MCP230_OUT\":{")); for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { if (Settings.mcp230xx_config[pinx].pinmode >= 5) { sprintf(stt,ConvertNumTxt(((gpiototal>>pinx)&1),Settings.mcp230xx_config[pinx].pinmode)); @@ -746,8 +744,7 @@ void MCP230xx_OutputTelemetry(void) { #endif // USE_MCP230xx_OUTPUT void MCP230xx_Interrupt_Counter_Report(void) { - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"MCP230_INTTIMER\":{")); + ResponseTime_P(PSTR(",\"MCP230_INTTIMER\":{")); for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { if (Settings.mcp230xx_config[pinx].int_count_en) { // Counting is enabled for this pin so we add to report ResponseAppend_P(PSTR("\"INTCNT_D%i\":%i,"),pinx,mcp230xx_int_counter[pinx]); @@ -761,8 +758,7 @@ void MCP230xx_Interrupt_Counter_Report(void) { void MCP230xx_Interrupt_Retain_Report(void) { uint16_t retainresult = 0; - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"MCP_INTRETAIN\":{")); + ResponseTime_P(PSTR(",\"MCP_INTRETAIN\":{")); for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { if (Settings.mcp230xx_config[pinx].int_retain_flag) { ResponseAppend_P(PSTR("\"D%i\":%i,"),pinx,mcp230xx_int_retainer[pinx]); diff --git a/sonoff/xsns_40_pn532.ino b/sonoff/xsns_40_pn532.ino index 17527aa7e..333ae7524 100644 --- a/sonoff/xsns_40_pn532.ino +++ b/sonoff/xsns_40_pn532.ino @@ -494,12 +494,10 @@ void PN532_ScanForTag(void) pn532_function = 0; #endif // USE_PN532_DATA_FUNCTION - ResponseBeginTime(); - #ifdef USE_PN532_DATA_FUNCTION - ResponseAppend_P(PSTR(",\"PN532\":{\"UID\":\"%s\", \"DATA\":\"%s\"}}"), uids, card_datas); + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\", \"DATA\":\"%s\"}}"), uids, card_datas); #else - ResponseAppend_P(PSTR(",\"PN532\":{\"UID\":\"%s\"}}"), uids); + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\"}}"), uids); #endif // USE_PN532_DATA_FUNCTION MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); @@ -541,8 +539,7 @@ bool PN532_Command(void) if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"E")) { pn532_function = 1; // Block 1 of next card/tag will be reset to 0x00... AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Next scanned tag data block 1 will be erased")); - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"PN532\":{\"COMMAND\":\"E\"}}")); + ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"E\"}}")); return serviced; } if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"S")) { @@ -558,8 +555,7 @@ bool PN532_Command(void) pn532_newdata[pn532_newdata_len] = 0x00; // Null terminate the string pn532_function = 2; AddLog_P2(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Next scanned tag data block 1 will be set to '%s'"), pn532_newdata); - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"PN532\":{\"COMMAND\":\"S\"}}")); + ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"S\"}}")); return serviced; } } diff --git a/sonoff/xsns_44_sps30.ino b/sonoff/xsns_44_sps30.ino index 1b1434044..20dc8a8f0 100644 --- a/sonoff/xsns_44_sps30.ino +++ b/sonoff/xsns_44_sps30.ino @@ -253,8 +253,7 @@ void SPS30_Show(bool json) { void CmdClean(void) { sps30_cmd(SPS_CMD_CLEAN); - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"SPS30\":{\"CFAN\":\"true\"}}")); + ResponseTime_P(PSTR(",\"SPS30\":{\"CFAN\":\"true\"}}")); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); } diff --git a/sonoff/xsns_51_rdm6300.ino b/sonoff/xsns_51_rdm6300.ino index 7fc168d7d..2a42b2a08 100644 --- a/sonoff/xsns_51_rdm6300.ino +++ b/sonoff/xsns_51_rdm6300.ino @@ -106,8 +106,7 @@ void RDM6300_ScanForTag() { memcpy(rdm_uid_str,&rdm_buffer[2],8); rdm_uid_str[9]=0; - Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); - ResponseAppend_P(PSTR(",\"RDM6300\":{\"UID\":\"%s\"}}"), rdm_uid_str); + ResponseTime_P(PSTR(",\"RDM6300\":{\"UID\":\"%s\"}}"), rdm_uid_str); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); /* char command[24]; diff --git a/sonoff/xsns_52_ibeacon.ino b/sonoff/xsns_52_ibeacon.ino index dbd56abd1..1df541d82 100644 --- a/sonoff/xsns_52_ibeacon.ino +++ b/sonoff/xsns_52_ibeacon.ino @@ -547,8 +547,7 @@ void ibeacon_mqtt(const char *mac,const char *rssi) { memcpy(s_rssi,rssi,4); s_rssi[4]=0; int16_t n_rssi=atoi(s_rssi); - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"" D_CMND_IBEACON "_%s\":{\"RSSI\":%d}}"),s_mac,n_rssi); + ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "_%s\":{\"RSSI\":%d}}"),s_mac,n_rssi); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); } diff --git a/sonoff/xsns_53_sml.ino b/sonoff/xsns_53_sml.ino index 3e0d56300..9dabb29cd 100644 --- a/sonoff/xsns_53_sml.ino +++ b/sonoff/xsns_53_sml.ino @@ -140,6 +140,11 @@ struct METER_DESC { uint16_t flag; int32_t params; char prefix[8]; + int8_t trxpin; + uint8_t tsecs; + char *txmem; + uint8_t index; + uint8_t max_index; }; // meter list , enter new meters here @@ -167,7 +172,7 @@ struct METER_DESC { #undef METERS_USED #define METERS_USED 1 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS"}}; + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; const uint8_t meter[]= "1,1-0:1.8.0*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" "1,1-0:2.8.0*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" @@ -185,7 +190,7 @@ const uint8_t meter[]= #undef METERS_USED #define METERS_USED 1 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS"}}; + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; const uint8_t meter[]= "1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" "1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" @@ -199,7 +204,7 @@ const uint8_t meter[]= #undef METERS_USED #define METERS_USED 1 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML"}}; + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; // 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär const uint8_t meter[]= //0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff @@ -218,7 +223,7 @@ const uint8_t meter[]= #undef METERS_USED #define METERS_USED 1 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML"}}; + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; // 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär // verbrauch total const uint8_t meter[]= @@ -236,7 +241,7 @@ const uint8_t meter[]= #undef METERS_USED #define METERS_USED 1 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML"}}; + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; // 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär // verbrauch total const uint8_t meter[]= @@ -252,7 +257,7 @@ const uint8_t meter[]= #undef METERS_USED #define METERS_USED 1 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"strom"}}; + [0]={3,'s',0,SML_BAUDRATE,"strom",-1,1,0}}; const uint8_t meter[]= //0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff "1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" @@ -275,7 +280,7 @@ const uint8_t meter[]= #undef METERS_USED #define METERS_USED 1 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML"}}; + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; const uint8_t meter[]= //0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff "1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" @@ -291,9 +296,9 @@ const uint8_t meter[]= #define METERS_USED 3 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS"}, // harware serial RX pin - [1]={14,'s',0,SML_BAUDRATE,"SML"}, // GPIO14 software serial - [2]={4,'o',0,SML_BAUDRATE,"OBIS2"}}; // GPIO4 software serial + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, // harware serial RX pin + [1]={14,'s',0,SML_BAUDRATE,"SML",-1,1,0}, // GPIO14 software serial + [2]={4,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}}; // GPIO4 software serial // 3 Zähler definiert const uint8_t meter[]= @@ -320,8 +325,8 @@ const uint8_t meter[]= #define METERS_USED 2 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS1"}, // harware serial RX pin - [1]={14,'o',0,SML_BAUDRATE,"OBIS2"}}; // GPIO14 software serial + [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, // harware serial RX pin + [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}}; // GPIO14 software serial // 2 Zähler definiert const uint8_t meter[]= @@ -342,9 +347,9 @@ const uint8_t meter[]= #define METERS_USED 3 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS1"}, // harware serial RX pin - [1]={14,'o',0,SML_BAUDRATE,"OBIS2"}, - [2]={1,'o',0,SML_BAUDRATE,"OBIS3"}}; + [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, // harware serial RX pin + [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}, + [2]={1,'o',0,SML_BAUDRATE,"OBIS3",-1,1,0}}; // 3 Zähler definiert const uint8_t meter[]= @@ -372,7 +377,7 @@ const uint8_t meter[]= #undef METERS_USED #define METERS_USED 1 struct METER_DESC const meter_desc[METERS_USED]={ -[0]={3,'o',0,SML_BAUDRATE,"OBIS"}}; +[0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; const uint8_t meter[]= "1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" "1,=d 1 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" @@ -385,7 +390,7 @@ const uint8_t meter[]= #undef METERS_USED #define METERS_USED 1 struct METER_DESC const meter_desc[METERS_USED]={ -[0]={3,'s',0,SML_BAUDRATE,"SML"}}; +[0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; // 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär const uint8_t meter[]= //0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff @@ -407,7 +412,7 @@ const uint8_t meter[]= #undef METERS_USED #define METERS_USED 3 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS"}, // harware serial RX pin + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, // harware serial RX pin [1]={14,'c',0,50,"Gas"}, // GPIO14 gas counter [2]={1,'c',0,10,"Wasser"}}; // water counter @@ -430,9 +435,9 @@ const uint8_t meter[]= #define METERS_USED 3 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={1,'c',0,10,"H20"}, // GPIO1 Wasser Zähler - [1]={4,'c',0,50,"GAS"}, // GPIO4 gas Zähler - [2]={3,'s',0,SML_BAUDRATE,"SML"}}; // SML harware serial RX pin + [0]={1,'c',0,10,"H20",-1,1,0}, // GPIO1 Wasser Zähler + [1]={4,'c',0,50,"GAS",-1,1,0}, // GPIO4 gas Zähler + [2]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; // SML harware serial RX pin const uint8_t meter[]= //----------------------------Wasserzähler--sensor53 c1------------------------------------ @@ -516,7 +521,7 @@ char meter_id[MAX_METERS][METER_ID_SIZE]; #define EBUS_SYNC 0xaa #define EBUS_ESC 0xa9 uint8_t ebus_pos; - +uint8_t mbus_pos; #ifdef USE_MEDIAN_FILTER // median filter, should be odd size @@ -786,32 +791,20 @@ uint8_t dump2log=0; bool Serial_available() { uint8_t num=dump2log&7; - if (num<1 || num>meters_used) return Serial.available(); - if (num==1) { - return Serial.available(); - } else { - return meter_ss[num-1]->available(); - } + if (num<1 || num>meters_used) num=1; + return meter_ss[num-1]->available(); } uint8_t Serial_read() { uint8_t num=dump2log&7; - if (num<1 || num>meters_used) return Serial.read(); - if (num==1) { - return Serial.read(); - } else { - return meter_ss[num-1]->read(); - } + if (num<1 || num>meters_used) num=1; + return meter_ss[num-1]->read(); } uint8_t Serial_peek() { uint8_t num=dump2log&7; - if (num<1 || num>meters_used) return Serial.peek(); - if (num==1) { - return Serial.peek(); - } else { - return meter_ss[num-1]->peek(); - } + if (num<1 || num>meters_used) num=1; + return meter_ss[num-1]->peek(); } void Dump2log(void) { @@ -1154,15 +1147,13 @@ uint8_t ebus_CalculateCRC( uint8_t *Data, uint16_t DataLen ) { void sml_shift_in(uint32_t meters,uint32_t shard) { uint32_t count; - if (meter_desc_p[meters].type!='e') { + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m') { // shift in for (count=0; countread(); + uint8_t iob=(uint8_t)meter_ss[meters]->read(); if (meter_desc_p[meters].type=='o') { smltbuf[meters][SML_BSIZ-1]=iob&0x7f; @@ -1170,6 +1161,13 @@ void sml_shift_in(uint32_t meters,uint32_t shard) { smltbuf[meters][SML_BSIZ-1]=iob; } else if (meter_desc_p[meters].type=='r') { smltbuf[meters][SML_BSIZ-1]=iob; + } else if (meter_desc_p[meters].type=='m') { + smltbuf[meters][mbus_pos] = iob; + mbus_pos++; + if (mbus_pos>=9) { + SML_Decode(meters); + mbus_pos=0; + } } else { if (iob==EBUS_SYNC) { // should be end of telegramm @@ -1200,7 +1198,7 @@ void sml_shift_in(uint32_t meters,uint32_t shard) { } } sb_counter++; - if (meter_desc_p[meters].type!='e') SML_Decode(meters); + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m') SML_Decode(meters); } @@ -1211,14 +1209,8 @@ uint32_t meters; for (meters=0; metersavailable()) { - sml_shift_in(meters,0); - } + while (meter_ss[meters]->available()) { + sml_shift_in(meters,0); } } } @@ -1348,7 +1340,8 @@ void SML_Decode(uint8_t index) { } else { // compare value uint8_t found=1; - int32_t ebus_dval=99; + uint32_t ebus_dval=99; + float mbus_dval=99; while (*mp!='@') { if (meter_desc_p[mindex].type=='o' || meter_desc_p[mindex].type=='c') { if (*mp++!=*cp++) { @@ -1363,7 +1356,7 @@ void SML_Decode(uint8_t index) { found=0; } } else { - // ebus or raw + // ebus mbus or raw // XXHHHHSSUU if (*mp=='x' && *(mp+1)=='x') { //ignore @@ -1379,7 +1372,7 @@ void SML_Decode(uint8_t index) { ebus_dval=val; mp+=2; } - else if (*mp=='s' && *(mp+1)=='s' && *(mp+2)=='s' && *(mp+3)=='s'){ + else if (*mp=='s' && *(mp+1)=='s' && *(mp+2)=='s' && *(mp+3)=='s') { int16_t val = *cp|(*(cp+1)<<8); ebus_dval=val; mp+=4; @@ -1389,7 +1382,15 @@ void SML_Decode(uint8_t index) { int8_t val = *cp++; ebus_dval=val; mp+=2; - } else { + } + else if (*mp=='f' && *(mp+1)=='f' && *(mp+2)=='f' && *(mp+3)=='f' && *(mp+4)=='f' && *(mp+5)=='f' && *(mp+6)=='f' && *(mp+7)=='f') { + uint32_t val= (*(cp+0)<<24)|(*(cp+1)<<16)|(*(cp+2)<<8)|(*(cp+3)<<0); + float *fp=(float*)&val; + mbus_dval=*fp; + mp+=8; + cp+=4; + } + else { uint8_t val = hexnibble(*mp++) << 4; val |= hexnibble(*mp++); if (val!=*cp++) { @@ -1418,7 +1419,7 @@ void SML_Decode(uint8_t index) { } } else { double dval; - if (meter_desc_p[mindex].type!='e' && meter_desc_p[mindex].type!='r') { + if (meter_desc_p[mindex].type!='e' && meter_desc_p[mindex].type!='r' && meter_desc_p[mindex].type!='m') { // get numeric values if (meter_desc_p[mindex].type=='o' || meter_desc_p[mindex].type=='c') { dval=xCharToDouble((char*)cp); @@ -1426,7 +1427,7 @@ void SML_Decode(uint8_t index) { dval=sml_getvalue(cp,mindex); } } else { - // ebus + // ebus or mbus if (*mp=='b') { mp++; uint8_t shift=*mp&7; @@ -1434,7 +1435,23 @@ void SML_Decode(uint8_t index) { ebus_dval&=1; mp+=2; } - dval=ebus_dval; + if (*mp=='i') { + // mbus index + mp++; + uint8_t mb_index=strtol((char*)mp,(char**)&mp,10); + if (mb_index!=meter_desc_p[mindex].index) { + goto nextsect; + } + uint16_t crc = MBUS_calculateCRC(&smltbuf[mindex][0],7); + if (lowByte(crc)!=smltbuf[mindex][7]) goto nextsect; + if (highByte(crc)!=smltbuf[mindex][8]) goto nextsect; + dval=mbus_dval; + //AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),mp); + mp++; + } else { + dval=ebus_dval; + } + } #ifdef USE_MEDIAN_FILTER meter_vars[vindex]=median(&sml_mf[vindex],dval); @@ -1488,8 +1505,7 @@ void SML_Immediate_MQTT(const char *mp,uint8_t index,uint8_t mindex) { if (dp&0x10) { // immediate mqtt dtostrfd(meter_vars[index],dp&0xf,tpowstr); - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%s}}"),meter_desc_p[mindex].prefix,jname,tpowstr); + ResponseTime_P(PSTR(",\"%s\":{\"%s\":%s}}"),meter_desc_p[mindex].prefix,jname,tpowstr); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); } } @@ -1723,6 +1739,14 @@ void SML_Init(void) { #ifdef USE_SCRIPT + + for (uint32_t cnt=0;cntM",-2,0); if (meter_script==99) { // use script definition @@ -1782,13 +1806,42 @@ void SML_Init(void) { lp++; script_meter_desc[index].prefix[7]=0; for (uint32_t cnt=0; cnt<8; cnt++) { - if (*lp==SCRIPT_EOL) { + if (*lp==SCRIPT_EOL || *lp==',') { script_meter_desc[index].prefix[cnt]=0; - lp--; break; } script_meter_desc[index].prefix[cnt]=*lp++; } + if (*lp==',') { + lp++; + script_meter_desc[index].trxpin=strtol(lp,&lp,10); + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].tsecs=strtol(lp,&lp,10); + if (*lp==',') { + lp++; + char txbuff[256]; + uint32_t txlen=0,tx_entries=1; + for (uint32_t cnt=0; cntbegin(meter_desc_p[meters].params)) { meter_ss[meters]->flush(); } - } + if (meter_ss[meters]->hardwareSerial()) { ClaimSerial(); } + } } @@ -1958,25 +2012,102 @@ uint32_t ctime=millis(); } } -#ifdef SML_SEND_SEQ -#define SML_SEQ_PERIOD 5 -uint8_t sml_seq_cnt; -void SendSeq(void) { - sml_seq_cnt++; - if (sml_seq_cnt>SML_SEQ_PERIOD) { - sml_seq_cnt=0; - // send sequence every N Seconds - uint8_t sequence[]={0x2F,0x3F,0x21,0x0D,0x0A,0}; - uint8_t *ucp=sequence; - while (*ucp) { - uint8_t iob=*ucp++; - // for no parity disable next line - iob|=(CalcEvenParity(iob)<<7); - Serial.write(iob); +#ifdef USE_SCRIPT +char *SML_Get_Sequence(char *cp,uint32_t index) { + if (!index) return cp; + uint32_t cindex=0; + while (cp) { + cp=strchr(cp,','); + if (cp) { + cp++; + cindex++; + if (cindex==index) { + return cp; + } } } } +uint8_t sml_250ms_cnt; + + +void SML_Check_Send(void) { + sml_250ms_cnt++; + for (uint32_t cnt=0; cnt=0 && script_meter_desc[cnt].txmem) { + if ((sml_250ms_cnt%script_meter_desc[cnt].tsecs)==0) { + if (script_meter_desc[cnt].max_index>1) { + script_meter_desc[cnt].index++; + if (script_meter_desc[cnt].index>=script_meter_desc[cnt].max_index) { + script_meter_desc[cnt].index=0; + } + char *cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index); + SML_Send_Seq(cnt,cp); + //AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),cp); + } else { + SML_Send_Seq(cnt,script_meter_desc[cnt].txmem); + } + } + } + } +} + +uint8_t sml_hexnibble(char chr) { + uint8_t rVal = 0; + if (isdigit(chr)) { + rVal = chr - '0'; + } else { + if (chr >= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; + if (chr >= 'a' && chr <= 'f') rVal = chr + 10 - 'a'; + } + return rVal; +} + +// send sequence every N Seconds +void SML_Send_Seq(uint32_t meter,char *seq) { + uint8_t sbuff[32]; + uint8_t *ucp=sbuff,slen; + char *cp=seq; + while (*cp) { + if (!*cp || !*(cp+1)) break; + if (*cp==',') break; + uint8_t iob=(sml_hexnibble(*cp) << 4) | sml_hexnibble(*(cp+1)); + cp+=2; + *ucp++=iob; + slen++; + if (slen>=sizeof(sbuff)) break; + } + if (script_meter_desc[meter].type=='m') { + *ucp++=0; + *ucp++=2; + // append crc + uint16_t crc = MBUS_calculateCRC(sbuff,6); + *ucp++=lowByte(crc); + *ucp++=highByte(crc); + slen+=4; + } + meter_ss[meter]->write(sbuff,slen); +} +#endif // USE_SCRIPT + +uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num) { + uint16_t crc, flag; + crc = 0xFFFF; + for (uint32_t i = 0; i < num; i++) { + crc ^= frame[i]; + for (uint32_t j = 8; j; j--) { + if ((crc & 0x0001) != 0) { // If the LSB is set + crc >>= 1; // Shift right and XOR 0xA001 + crc ^= 0xA001; + } else { // Else LSB is not set + crc >>= 1; // Just shift right + } + } + } + return crc; +} + +/* // for odd parity init with 1 uint8_t CalcEvenParity(uint8_t data) { uint8_t parity=0; @@ -1987,7 +2118,7 @@ uint8_t parity=0; } return parity; } -#endif +*/ // dump to log shows serial data on console @@ -2004,8 +2135,7 @@ bool XSNS_53_cmd(void) { // set dump mode cp++; dump2log=atoi(cp); - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"SML\":{\"CMD\":\"dump: %d\"}}"),dump2log); + ResponseTime_P(PSTR(",\"SML\":{\"CMD\":\"dump: %d\"}}"),dump2log); } else if (*cp=='c') { // set ounter cp++; @@ -2025,12 +2155,10 @@ bool XSNS_53_cmd(void) { } } } - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"SML\":{\"CMD\":\"counter%d: %d\"}}"),index,RtcSettings.pulse_counter[index-1]); + ResponseTime_P(PSTR(",\"SML\":{\"CMD\":\"counter%d: %d\"}}"),index,RtcSettings.pulse_counter[index-1]); } else if (*cp=='r') { // restart - ResponseBeginTime(); - ResponseAppend_P(PSTR(",\"SML\":{\"CMD\":\"restart\"}}")); + ResponseTime_P(PSTR(",\"SML\":{\"CMD\":\"restart\"}}")); SML_CounterSaveState(); SML_Init(); } else { @@ -2053,6 +2181,7 @@ void SML_CounterSaveState(void) { + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -2070,11 +2199,11 @@ bool Xsns53(byte function) { if (dump2log) Dump2log(); else SML_Poll(); break; -#ifdef SML_SEND_SEQ - case FUNC_EVERY_SECOND: - SendSeq(); +#ifdef USE_SCRIPT + case FUNC_EVERY_250_MSECOND: + SML_Check_Send(); break; -#endif +#endif // USE_SCRIPT case FUNC_JSON_APPEND: SML_Show(1); break; diff --git a/tools/decode-config.py b/tools/decode-config.py index 67e21b3a6..f04854acd 100755 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -VER = '2.3.0032' +VER = '2.3.0033' """ decode-config.py - Backup/Restore Sonoff-Tasmota configuration data @@ -984,7 +984,22 @@ Setting_6_6_0_9.update ({ 'sbaudrate': ('